मैं यह कैसे बता सकता हूं कि पायथन में एक स्ट्रिंग अपने आप को दोहराती है या नहीं।


352

मैं यह जांचने का एक तरीका ढूंढ रहा हूं कि क्या दिया गया तार पूरे स्ट्रिंग के लिए खुद को दोहराता है या नहीं।

उदाहरण:

[
    '0045662100456621004566210045662100456621',             # '00456621'
    '0072992700729927007299270072992700729927',             # '00729927'
    '001443001443001443001443001443001443001443',           # '001443'
    '037037037037037037037037037037037037037037037',        # '037'
    '047619047619047619047619047619047619047619',           # '047619'
    '002457002457002457002457002457002457002457',           # '002457'
    '001221001221001221001221001221001221001221',           # '001221'
    '001230012300123001230012300123001230012300123',        # '00123'
    '0013947001394700139470013947001394700139470013947',    # '0013947'
    '001001001001001001001001001001001001001001001001001',  # '001'
    '001406469760900140646976090014064697609',              # '0014064697609'
]

तार हैं जो खुद को दोहराते हैं, और

[
    '004608294930875576036866359447',
    '00469483568075117370892018779342723',
    '004739336492890995260663507109',
    '001508295625942684766214177978883861236802413273',
    '007518796992481203',
    '0071942446043165467625899280575539568345323741',
    '0434782608695652173913',
    '0344827586206896551724137931',
    '002481389578163771712158808933',
    '002932551319648093841642228739',
    '0035587188612099644128113879',
    '003484320557491289198606271777',
    '00115074798619102416570771',
]

उन लोगों के उदाहरण हैं जो नहीं करते हैं।

मेरे द्वारा दिए गए स्ट्रिंग्स के दोहराए जाने वाले खंड काफी लंबे हो सकते हैं, और स्ट्रिंग्स स्वयं 500 या अधिक वर्ण के हो सकते हैं, इसलिए प्रत्येक चरित्र के माध्यम से लूपिंग एक पैटर्न बनाने की कोशिश कर रहा है फिर पैटर्न की जाँच करना बनाम बाकी स्ट्रिंग बहुत भयानक लगता है। गुणा करें कि संभावित रूप से सैकड़ों तार और मैं कोई सहज समाधान नहीं देख सकता।

मैंने थोड़ा सा regexes में देखा है और वे तब अच्छे लगते हैं जब आप जानते हैं कि आप क्या देख रहे हैं, या कम से कम पैटर्न की लंबाई की तलाश में हैं। दुर्भाग्य से, मुझे न तो पता है।

मैं कैसे बता सकता हूं कि क्या एक स्ट्रिंग खुद को दोहरा रही है और अगर यह है, तो सबसे कम दोहराव क्या है?


15
प्रत्येक चरित्र के माध्यम से लूपिंग एक पैटर्न बनाने की कोशिश कर रहा है फिर पैटर्न की जाँच करना बनाम बाकी स्ट्रिंग बहुत भयानक लगता है - लेकिन क्या यह है?
टिम


2
@AashashRaj यह एक स्ट्रिंग का केवल एक मेल है, पूरी बात नहीं।
जॉन

11
@AvinashRaj ओपी सभी संभावित समाधानों के बारे में पूछ रहा है। जो प्रश्न आप केवल रेगेक्स समाधान को स्वीकार करते हैं, उससे जुड़ा हुआ है । नोट regex लेकिन समस्या का समाधान करने में सक्षम हो सकता है कि बहुत आवश्यकता से अधिक समय। उदाहरण के लिए एक इष्टतम समाधान (यानी रैखिक समय) पाठ के प्रत्यय पेड़ का उपयोग करेगा। आपको बस सबसे लंबे समय तक दोहराया जाने वाला सबस्ट्रिंग ढूंढना है और लंबाई पर कुछ जांच करनी है।
बकुरीउ

2
@ TigerhawkT3 असली डेटासेट अब तक बहुत बड़ा और अनपेक्षित है, लेकिन सवाल में उदाहरण इसका हिस्सा हैं, और यदि आप चाहें, तो यहां कुछ और है
जॉन

जवाबों:


570

यहाँ एक संक्षिप्त समाधान है जो नियमित रूप से अभिव्यक्ति से बचता है और पायथन लूप्स में धीमा है:

def principal_period(s):
    i = (s+s).find(s, 1, -1)
    return None if i == -1 else s[:i]

बेंचमार्क परिणामों के लिए @davidism द्वारा शुरू किया गया सामुदायिक विकी उत्तर देखें । संक्षेप में,

डेविड झांग का समाधान स्पष्ट विजेता है, जो बड़े उदाहरण सेट के लिए कम से कम 5x से सभी को पछाड़ता है।

(यह जवाब शब्दों में है, मेरा नहीं।)

यह इस अवलोकन पर आधारित है कि यदि कोई स्ट्रिंग आवधिक है और केवल तभी है जब वह स्वयं के एक निरुपित घुमाव के बराबर हो। एहसास है कि हम तो की पहली आवृत्ति के सूचकांक से प्रिंसिपल अवधि के लिए ठीक हो सकता है @AleksiTorhamo को कुडोस sमें (s+s)[1:-1], और मुझे वैकल्पिक की जानकारी देने के लिए startऔर endअजगर के के तर्कों string.find


19
आप उदाहरण के लिए .find()या .index()इसके बजाय का उपयोग करके सबसे कम दोहराव वाले स्थान को खोजने के लिए इसे बढ़ा सकते हैं in(s+s).find(s, 1, -1)
अलेक्सी तोराहो

11
इसके अलावा, मुझे लगता है कि (s+s).find(s, 1, -1)(बहुत थोड़ा) तेजी से (s+s)[1:-1].find(s), कम से कम बड़े तारों के लिए होगा, क्योंकि स्लाइसिंग का मतलब है कि आपको पूरी स्ट्रिंग की (लगभग) एक और प्रतिलिपि बनानी होगी।
एलेक्सी तोरामो

13
यह ऐसा है जैसे यदि आप एक आवधिक कार्य से एक पाप या कोस तरंग लेते हैं और इसे दाईं ओर स्थानांतरित करते हैं। चूंकि यह पूरी तरह से आवधिक है, इसलिए तरंगें अंततः पूरी तरह से मेल खाएंगी ... इस समाधान के गणित समानताएं केवल अभूतपूर्व हैं। :) काश मैं तुम्हें + wish upvotes दे पाता।
शशांक

6
PEP 8 के लिए गुइडो का हालिया अपडेट यहां प्रासंगिक है: "रिटर्न स्टेटमेंट में सुसंगत रहें। किसी फ़ंक्शन में सभी रिटर्न स्टेटमेंट को एक एक्सप्रेशन वापस करना चाहिए, या उनमें से कोई भी नहीं होना चाहिए। यदि कोई रिटर्न स्टेटमेंट एक एक्सप्रेशन देता है, तो कोई भी रिटर्न स्टेटमेंट जहां कोई मूल्य नहीं है। रिटर्न को स्पष्ट रूप से रिटर्न एनी के रूप में निर्दिष्ट करना चाहिए , और फ़ंक्शन के अंत में एक स्पष्ट रिटर्न स्टेटमेंट मौजूद होना चाहिए (यदि पहुंच योग्य है)। "
जीरो पिरियस

8
@WayneConrad एक स्ट्रिंग लें, कहें, "abcd"वर्ण को दाईं ओर पॉप करें, और इसे प्राप्त करने के लिए बाईं ओर चिपका दें "dabc"। इस प्रक्रिया को एक स्ट्रिंग को दाईं ओर 1 वर्ण से घुमाना कहा जाता है । दोहराएँ nगुना से सही करने के लिए एक स्ट्रिंग को घुमाने के लिए nवर्ण। अब निरीक्षण करें कि यदि हमारे पास kवर्णों की एक स्ट्रिंग है , kतो स्ट्रिंग को अपरिवर्तित किसी भी कई पत्तियों द्वारा दाईं ओर घुमाया जाता है । एक स्ट्रिंग का एक नॉनट्रिविअल रोटेशन वह है जिसका वर्ण संख्या स्ट्रिंग की लंबाई के एक से अधिक नहीं है।
डेविड झांग

181

यहाँ नियमित अभिव्यक्ति का उपयोग कर एक समाधान है।

import re

REPEATER = re.compile(r"(.+?)\1+$")

def repeated(s):
    match = REPEATER.match(s)
    return match.group(1) if match else None

प्रश्न में उदाहरणों के बारे में बताना:

examples = [
    '0045662100456621004566210045662100456621',
    '0072992700729927007299270072992700729927',
    '001443001443001443001443001443001443001443',
    '037037037037037037037037037037037037037037037',
    '047619047619047619047619047619047619047619',
    '002457002457002457002457002457002457002457',
    '001221001221001221001221001221001221001221',
    '001230012300123001230012300123001230012300123',
    '0013947001394700139470013947001394700139470013947',
    '001001001001001001001001001001001001001001001001001',
    '001406469760900140646976090014064697609',
    '004608294930875576036866359447',
    '00469483568075117370892018779342723',
    '004739336492890995260663507109',
    '001508295625942684766214177978883861236802413273',
    '007518796992481203',
    '0071942446043165467625899280575539568345323741',
    '0434782608695652173913',
    '0344827586206896551724137931',
    '002481389578163771712158808933',
    '002932551319648093841642228739',
    '0035587188612099644128113879',
    '003484320557491289198606271777',
    '00115074798619102416570771',
]

for e in examples:
    sub = repeated(e)
    if sub:
        print("%r: %r" % (e, sub))
    else:
        print("%r does not repeat." % e)

... इस उत्पादन का उत्पादन:

'0045662100456621004566210045662100456621': '00456621'
'0072992700729927007299270072992700729927': '00729927'
'001443001443001443001443001443001443001443': '001443'
'037037037037037037037037037037037037037037037': '037'
'047619047619047619047619047619047619047619': '047619'
'002457002457002457002457002457002457002457': '002457'
'001221001221001221001221001221001221001221': '001221'
'001230012300123001230012300123001230012300123': '00123'
'0013947001394700139470013947001394700139470013947': '0013947'
'001001001001001001001001001001001001001001001001001': '001'
'001406469760900140646976090014064697609': '0014064697609'
'004608294930875576036866359447' does not repeat.
'00469483568075117370892018779342723' does not repeat.
'004739336492890995260663507109' does not repeat.
'001508295625942684766214177978883861236802413273' does not repeat.
'007518796992481203' does not repeat.
'0071942446043165467625899280575539568345323741' does not repeat.
'0434782608695652173913' does not repeat.
'0344827586206896551724137931' does not repeat.
'002481389578163771712158808933' does not repeat.
'002932551319648093841642228739' does not repeat.
'0035587188612099644128113879' does not repeat.
'003484320557491289198606271777' does not repeat.
'00115074798619102416570771' does not repeat.

नियमित अभिव्यक्ति (.+?)\1+$को तीन भागों में विभाजित किया गया है:

  1. (.+?)किसी भी चरित्र के कम से कम एक (लेकिन जितना संभव हो कम) वाला एक मिलान समूह है (क्योंकि +?गैर-लालची है )।

  2. \1+ पहले भाग में मिलान समूह के कम से कम एक पुनरावृत्ति के लिए जाँच।

  3. $स्ट्रिंग के अंत के लिए जाँच करता है, यह सुनिश्चित करने के लिए कि दोहराए गए सबस्ट्रिंग के बाद कोई अतिरिक्त, गैर-दोहराई जाने वाली सामग्री नहीं है (और re.match()यह सुनिश्चित करने का उपयोग करता है कि दोहराए गए सबस्ट्रिंग से पहले कोई गैर-दोहराए जाने वाला पाठ न हो )।

पायथन 3.4 और बाद में, आप इसके बजाय ड्रॉप कर सकते हैं $और उपयोग कर सकते हैं re.fullmatch(), या (किसी भी पायथन में कम से कम 2.3 के रूप में) दूसरे तरीके से जा सकते हैं और re.search()रेगेक्स के साथ उपयोग कर सकते हैं ^(.+?)\1+$, जो सभी किसी भी चीज़ की तुलना में व्यक्तिगत स्वाद के लिए अधिक नीचे हैं।


6
मुझे नहीं पता कि यह संक्षिप्त दो लाइनर सबसे ज्यादा वोट देने वाला जवाब क्यों नहीं है। अन्य उत्तर खराब नहीं हैं, लेकिन यह बहुत बेहतर है। (यह अक्सर निरूपित नियमित अभिव्यक्ति का उपयोग कर सकता है , लेकिन मैं इस आसान का निरीक्षण अन्य बहुत लंबे उत्तरों की तुलना में कर सकता हूं, जो नेस्टेड ब्लॉक, संभावित टाइपो, ऑफ-बाय-वन त्रुटियों आदि से भरे हुए हैं) आह अच्छी तरह से, बदतर बेहतर है मुझे लगता है।
पॉल ड्रेपर

9
मुझे लगता है कि इसके दो मुख्य कारण हैं: 1) कुछ प्रोग्रामर गणित जैसे कि उनसे अधिक पसंद करते हैं जैसे कि वे रेगेक्स, और 2) चूंकि इनपुट स्ट्रिंग्स की लंबाई और प्रकृति अलग-अलग होती है, अलग-अलग उत्तर प्रदर्शन पर आगे खींचते हैं, सुपर-लॉन्ग एज केस स्ट्रिंग्स (जो वास्तविक डेटा में भी दिखाई नहीं दे सकता है) इस समाधान को उप-प्रस्तुत करने योग्य बनाएं।
TigerhawkT3

कभी-कभी आप नियमित अभिव्यक्तियों के प्रति बहुत पूर्वाग्रह का सामना करते हैं। Ive के पास 2 प्रबंधक थे जो नियमित अभिव्यक्तियों के उपयोग को मना करते थे क्योंकि उन्होंने सुना था कि नियमित एक्सप्रेसन काम के लिए गलत उपकरण हैं।
ऑफकोर्स

1
@PaulDraper: लगता है कि दृश्य के पीछे रेगेक्स क्या कर रहा है? यह स्ट्रिंग को पार्स कर रहा है और एक पॉसिबल रीपिटेशन मैच होने तक प्रत्येक तत्व को स्टोर करता है। ओपी के रूप में एक ही है कि बहुत धीमी गति से वही। सिर्फ इसलिए कि यह एक 2 लाइनर है कोई प्रदर्शन जीत नहीं है।
dhein

2
@Zaibis, मैं सामान्य रूप से सहमत हूँ, लेकिन यह डेविड के लिए सबसे छोटा और सबसे तेज़ समाधान ( stackoverflow.com/a/29482936/1212596)....Except दोनों है , जो मैंने उस टिप्पणी के बाद पोस्ट किया था। मैं वास्तव में डेविड के दृष्टिकोण को अधिक पसंद करता हूं (चतुर!)।
पॉल ड्रेपर

90

आप अवलोकन कर सकते हैं कि एक स्ट्रिंग को दोहराए जाने के लिए, इसकी लंबाई को उसके दोहराया अनुक्रम की लंबाई से विभाज्य होना चाहिए। यह देखते हुए, यहां एक समाधान है जो लंबाई के विभाजकों 1को n / 2समावेशी से उत्पन्न करता है, मूल स्ट्रिंग को विभाजकों की लंबाई के साथ सब्सट्रिंग में विभाजित करता है, और परिणाम सेट की समानता का परीक्षण करता है:

from math import sqrt, floor

def divquot(n):
    if n > 1:
        yield 1, n
    swapped = []
    for d in range(2, int(floor(sqrt(n))) + 1):
        q, r = divmod(n, d)
        if r == 0:
            yield d, q
            swapped.append((q, d))
    while swapped:
        yield swapped.pop()

def repeats(s):
    n = len(s)
    for d, q in divquot(n):
        sl = s[0:d]
        if sl * q == s:
            return sl
    return None

EDIT: पायथन 3 में, /ऑपरेटर डिफ़ॉल्ट रूप से फ्लोट डिवीजन करने के लिए बदल गया है। intपायथन 2 से विभाजन प्राप्त करने के लिए , आप //इसके बजाय ऑपरेटर का उपयोग कर सकते हैं । इसे मेरे ध्यान में लाने के लिए @ TigerhawkT3 को धन्यवाद।

//दोनों अजगर 2 और अजगर 3 में विभाजन पूर्णांक ऑपरेटर प्रदर्शन, इसलिए मैं इस सवाल का जवाब दोनों संस्करणों का समर्थन करने के नवीनीकृत किया है। वह हिस्सा जहां हम यह देखने के लिए परीक्षण करते हैं कि क्या सभी सब्सट्रिंग्स समान हैं, अब एक शॉर्ट-सर्कुलेटिंग ऑपरेशन का उपयोग कर allऔर एक जनरेटर अभिव्यक्ति है।

अद्यतन: मूल प्रश्न में परिवर्तन के जवाब में, कोड अब अद्यतन किया गया है कि यदि यह मौजूद है और Noneयदि यह नहीं है तो सबसे छोटा दोहराव वापस लौटाया जाए । @godlygeek ने जेनरेटर divmodपर पुनरावृत्तियों की संख्या को कम करने के लिए उपयोग करने का सुझाव दिया है divisors, और कोड को अपडेट करने के लिए अद्यतन किया गया है। अब यह nआरोही क्रम में सभी सकारात्मक विभाजक देता है , विशेष रूप से n

उच्च प्रदर्शन के लिए और अधिक अद्यतन: कई परीक्षणों के बाद, मैं इस निष्कर्ष पर पहुंचा हूं कि स्ट्रिंग समानता के लिए बस परीक्षण में पायथन में किसी भी स्लाइसिंग या इटेटर समाधान से बाहर का सबसे अच्छा प्रदर्शन है। इस प्रकार, मैंने @ TigerhawkT3 की पुस्तक से एक पत्ता निकाला और अपने समाधान को अद्यतन किया। यह अब पहले की तुलना में 6 गुना अधिक तेज है, टाइगरहॉक के समाधान की तुलना में काफी तेज है लेकिन डेविड की तुलना में धीमी है।


3
यह समाधान अद्भुत है। आप उत्पादन-उपभोक्ता पैटर्न का पालन करने के लिए भाजक विधि बदल सकते हैं। ताकि यह परिणाम के रूप में वे पाए जाते हैं। अगर यह उच्चतम जवाब नहीं है तो शर्म की बात होगी। बाकी सब कुछ क्रूर बल है।
जस्टिन डैनियल्सन

3
@JustinDanielson यह एक जेनरेटर एक्सप्रेशन से निर्मित जेनरेटर ऑब्जेक्ट देता है, जो एक निहित निर्माता है :) यह डिवाइडर को आलसी-मूल्यांकन करेगा।
शशांक

1
ओह। मुझे नहीं पता था। खैर, तब भी बेहतर। : DI समझते हैं कि आप sqrt से क्यों बचना चाहते हैं, लेकिन आप विभाजक श्रेणी के लिए n / 2 को ऊपरी बाउंड के रूप में उपयोग कर सकते हैं।
जस्टिन डैनियल्सन

1
@JustinDanielson सुझाव के लिए धन्यवाद, ऊपरी सीमा अब (n/2)समावेशी है।
शशांक

1
चाहिए n / 2में divisors()हो n // 2?
TigerhawkT3

87

इस प्रश्न के विभिन्न उत्तरों के लिए कुछ बेंचमार्क दिए गए हैं। कुछ आश्चर्यजनक परिणाम थे, जिनमें स्ट्रिंग परीक्षण के आधार पर बेतहाशा अलग-अलग प्रदर्शन शामिल थे।

कुछ कार्य (मुख्य रूप से बदल कर अजगर 3 के साथ काम करने के लिए संशोधित किया गया /साथ //पूर्णांक विभाजन सुनिश्चित करने के लिए)। यदि आप कुछ गलत देखते हैं, तो अपने फ़ंक्शन को जोड़ना चाहते हैं, या पायथन चैट रूम में @ZeroPiraeus को पिंग करना चाहते हैं ।

सारांश में: ओपी द्वारा यहां ( इस टिप्पणी के माध्यम से ) आपूर्ति किए गए उदाहरण डेटा के बड़े सेट के लिए सबसे अच्छे और सबसे खराब प्रदर्शन वाले समाधान के बीच लगभग 50x अंतर है । डेविड झांग का समाधान स्पष्ट विजेता है, बड़े उदाहरण सेट के लिए लगभग 5x तक सभी अन्य को पछाड़ते हैं।

बहुत बड़े "नो मैच" मामलों में उत्तरों की एक जोड़ी बहुत धीमी है। अन्यथा, परीक्षण के आधार पर फ़ंक्शन समान रूप से मिलान या स्पष्ट विजेता प्रतीत होते हैं।

यहाँ विभिन्न वितरणों को दर्शाने के लिए मैटलपोटलिब और सीबॉर्न के उपयोग से बने भूखंडों सहित परिणाम दिए गए हैं:


कॉर्पस 1 (आपूर्ति किए गए उदाहरण - छोटे सेट)

mean performance:
 0.0003  david_zhang
 0.0009  zero
 0.0013  antti
 0.0013  tigerhawk_2
 0.0015  carpetpython
 0.0029  tigerhawk_1
 0.0031  davidism
 0.0035  saksham
 0.0046  shashank
 0.0052  riad
 0.0056  piotr

median performance:
 0.0003  david_zhang
 0.0008  zero
 0.0013  antti
 0.0013  tigerhawk_2
 0.0014  carpetpython
 0.0027  tigerhawk_1
 0.0031  davidism
 0.0038  saksham
 0.0044  shashank
 0.0054  riad
 0.0058  piotr

कॉर्पस 1 ग्राफ


कॉर्पस 2 (आपूर्ति किए गए उदाहरण - बड़े सेट)

mean performance:
 0.0006  david_zhang
 0.0036  tigerhawk_2
 0.0036  antti
 0.0037  zero
 0.0039  carpetpython
 0.0052  shashank
 0.0056  piotr
 0.0066  davidism
 0.0120  tigerhawk_1
 0.0177  riad
 0.0283  saksham

median performance:
 0.0004  david_zhang
 0.0018  zero
 0.0022  tigerhawk_2
 0.0022  antti
 0.0024  carpetpython
 0.0043  davidism
 0.0049  shashank
 0.0055  piotr
 0.0061  tigerhawk_1
 0.0077  riad
 0.0109  saksham

कॉर्पस 1 ग्राफ


कॉर्पस 3 (बढ़त मामले)

mean performance:
 0.0123  shashank
 0.0375  david_zhang
 0.0376  piotr
 0.0394  carpetpython
 0.0479  antti
 0.0488  tigerhawk_2
 0.2269  tigerhawk_1
 0.2336  davidism
 0.7239  saksham
 3.6265  zero
 6.0111  riad

median performance:
 0.0107  tigerhawk_2
 0.0108  antti
 0.0109  carpetpython
 0.0135  david_zhang
 0.0137  tigerhawk_1
 0.0150  shashank
 0.0229  saksham
 0.0255  piotr
 0.0721  davidism
 0.1080  zero
 1.8539  riad

कॉर्पस 3 ग्राफ


परीक्षण और कच्चे परिणाम यहां उपलब्ध हैं


37

गैर-रेगेक्स समाधान:

def repeat(string):
    for i in range(1, len(string)//2+1):
        if not len(string)%len(string[0:i]) and string[0:i]*(len(string)//len(string[0:i])) == string:
            return string[0:i]

तेज़ गैर-रेगेक्स समाधान, @ThatWeirdo के लिए धन्यवाद (टिप्पणियां देखें):

def repeat(string):
    l = len(string)
    for i in range(1, len(string)//2+1):
        if l%i: continue
        s = string[0:i]
        if s*(l//i) == string:
            return s

उपरोक्त समाधान मूल रूप से कुछ प्रतिशत की तुलना में बहुत कम धीमा है, लेकिन यह आमतौर पर एक अच्छा सा तेज है - कभी-कभी बहुत अधिक तेजी से। यह अभी भी लंबे समय तक स्ट्रिंग्स के लिए डेविडिज़्म से तेज़ नहीं है, और शॉर्ट स्ट्रिंग्स के लिए शून्य का रेगेक्स समाधान बेहतर है। यह लगभग 1000-1500 वर्णों के तार के साथ सबसे तेज़ (गितुब पर davidism के परीक्षण के अनुसार - उसका उत्तर देखें) के लिए निकलता है। इसके बावजूद, यह मेरे द्वारा परीक्षण किए गए सभी मामलों में मज़बूती से दूसरा सबसे तेज़ (या बेहतर) है। धन्यवाद, ThatWeirdo।

परीक्षा:

print(repeat('009009009'))
print(repeat('254725472547'))
print(repeat('abcdeabcdeabcdeabcde'))
print(repeat('abcdefg'))
print(repeat('09099099909999'))
print(repeat('02589675192'))

परिणाम:

009
2547
abcde
None
None
None

क्या यह एक क्रूर बल समाधान नहीं है?
जस्टिन डैनियल्सन

7
@JustinDanielson हाँ। लेकिन एक समाधान कम नहीं है।
सिंकिंगपॉइंट

3
मैं छोटी स्ट्रिंग्स के लिए 1e-5 से 3e-5 सेकंड, सफल लंबे (1000-वर्ण) स्ट्रिंग्स के लिए 3e-5 से 4e-5 सेकंड और असफल लंबी स्ट्रिंग्स (सबसे खराब स्थिति) के लिए मिलीसेकंड के तहत थोड़ा सा देख रहा हूं । इसलिए एक हजार 1000 वर्णों के तार को लगभग एक सेकंड का समय लगेगा। गणित के उत्तर की तुलना में, यह 10 गुना तेजी से मेल खाता है, लेकिन असफल होने में 3 गुना अधिक समय लगता है।
टाइगरहॉक 3

repeat('aa')रिटर्नNone
टॉम कॉर्निज

2
len(string[0:i])हमेशा i(इस मामले में कम से कम) के बराबर है । इनकी जगह, और बचत len(string)और string[0:i]चर में भी चीजों की गति बढ़ सकती है। इसके अलावा IMO यह एक बढ़िया उपाय है, कमाल;)
ThatWeirdo

24

सबसे पहले, स्ट्रिंग को तब तक आधा करें जब तक कि यह "2 भाग" डुप्लिकेट न हो। यह खोज स्थान को कम कर देता है यदि दोहराव की एक समान संख्या होती है। फिर, सबसे छोटे दोहराए जाने वाले स्ट्रिंग को खोजने के लिए आगे की ओर काम करते हुए, जांचें कि क्या केवल खाली मूल्यों में तेजी से बड़े उप-स्ट्रिंग परिणामों से पूर्ण स्ट्रिंग को विभाजित करना है। केवल उप-स्ट्रिंग्स का length // 2परीक्षण करने की आवश्यकता है क्योंकि किसी भी चीज का दोहराव नहीं होगा।

def shortest_repeat(orig_value):
    if not orig_value:
        return None

    value = orig_value

    while True:
        len_half = len(value) // 2
        first_half = value[:len_half]

        if first_half != value[len_half:]:
            break

        value = first_half

    len_value = len(value)
    split = value.split

    for i in (i for i in range(1, len_value // 2) if len_value % i == 0):
        if not any(split(value[:i])):
            return value[:i]

    return value if value != orig_value else None

यह सबसे छोटा मैच लौटाता है या कोई भी मैच नहीं होता है।


16

O(n)उपसर्ग फ़ंक्शन के साथ सबसे खराब स्थिति में भी समस्या हल हो सकती है ।

ध्यान दें, यह सामान्य मामलों में धीमा हो सकता है (UPD: और बहुत धीमा है) अन्य समाधानों की संख्या पर निर्भर करता है n, लेकिन आमतौर पर यह जल्दी ही विफल हो जाता है, मुझे लगता है कि उनके लिए बुरे मामलों में से एक होगा aaa....aab, जहां हैं n - 1 = 2 * 3 * 5 * 7 ... *p_n - 1 a

सबसे पहले आपको उपसर्ग फ़ंक्शन की गणना करने की आवश्यकता है

def prefix_function(s):
    n = len(s)
    pi = [0] * n
    for i in xrange(1, n):
        j = pi[i - 1]
        while(j > 0 and s[i] != s[j]):
            j = pi[j - 1]
        if (s[i] == s[j]):
            j += 1
        pi[i] = j;
    return pi

तो या तो कोई जवाब नहीं है या सबसे छोटी अवधि है

k = len(s) - prefix_function(s[-1])

और आपको बस जांच करनी है कि क्या k != n and n % k == 0(यदि k != n and n % k == 0उत्तर है तो s[:k], और कोई उत्तर नहीं है

आप यहां प्रमाण की जांच कर सकते हैं (रूसी में, लेकिन ऑनलाइन अनुवादक शायद चाल करेगा)

def riad(s):
    n = len(s)
    pi = [0] * n
    for i in xrange(1, n):
        j = pi[i - 1]
        while(j > 0 and s[i] != s[j]):
            j = pi[j - 1]
        if (s[i] == s[j]):
            j += 1
        pi[i] = j;
    k = n - pi[-1]
    return s[:k] if (n != k and n % k == 0) else None

आपका prefix_function()मान्य पायथन नहीं है: आपके पास अपने बयानों whileऔर ifबयानों के &&बजाय गायब कॉलोन हैं and। उन को ठीक करने के बाद, यह UnboundLocalError: local variable 'i' referenced before assignmentरेखा के कारण विफल हो जाता है for i in range(i, n):
शून्य पीरियस

धन्यवाद :-) यदि आप एक फ़ंक्शन को एक साथ रख सकते हैं prefix_function()जो अन्य उत्तरों के समान परिणामों को वापस करने के लिए उपयोग करता है - या तो सबसे छोटा विकल्प या None- मैं इसे एक संशोधित बेंचमार्क में शामिल करूँगा जो मैं एक साथ डाल रहा हूं।
जीरो पिरियस

@ZeroPiraeus, यह वास्तव में ठीक काम करता है, मैंने अभी इसे गलत तरीके से बुलाया है
RiaD

16

यह संस्करण केवल उन उम्मीदवार अनुक्रम लंबाई की कोशिश करता है जो स्ट्रिंग लंबाई के कारक हैं; और *उम्मीदवार अनुक्रम से एक पूर्ण लंबाई स्ट्रिंग बनाने के लिए ऑपरेटर का उपयोग करता है :

def get_shortest_repeat(string):
    length = len(string)
    for i in range(1, length // 2 + 1):
        if length % i:  # skip non-factors early
            continue

        candidate = string[:i]
        if string == candidate * (length // i):
            return candidate

    return None

टाइगरहॉकटी 3 को यह ध्यान देने के लिए धन्यवाद कि length // 2बिना मामला दर्ज किए + 1असफल हो जाएगा abab


यह समाधान वास्तव में मेरे अनुकूलित एक के समान है। मुझे लगता है कि आपके पास एक rangeसीमा है length//2, जैसे मैंने किया - आपको इसे बदलना होगा length//2+1यदि आप स्ट्रिंग्स को पकड़ना चाहते हैं जो वास्तव में दोगुना है (जैसे 'aabaab')।
टाइगरहावटी 3

और अब वे समान हैं! \ o / मुझे भविष्य में अनुकूलन पर अधिक ध्यान देने की आवश्यकता है, लेकिन मुझे खुशी है कि एल्गोरिथ्म स्वयं ध्वनि था।
TigerhawkT3

15

रेगेक्स के बिना, एक सीधे आगे समाधान है।

sज़ीरोथ इंडेक्स से शुरू होने वाले सबस्ट्रिंग के लिए , लंबाई 1 के माध्यम से len(s), जांचें कि क्या प्रतिस्थापन substrहै, दोहराया पैटर्न है। इस चेक को substrस्वयं के ratioसमय के साथ समवर्ती करके किया जा सकता है , जैसे कि इस प्रकार बनाई गई स्ट्रिंग की लंबाई की लंबाई के बराबर है s। इसलिए ratio=len(s)/len(substr)

जब इस तरह का सबस्ट्रिंग पाया जाता है तो वापस लौटें। यह सबसे छोटा संभव विकल्प प्रदान करेगा, यदि कोई मौजूद है।

def check_repeat(s):
    for i in range(1, len(s)):
        substr = s[:i]
        ratio = len(s)/len(substr)
        if substr * ratio == s:
            print 'Repeating on "%s"' % substr
            return
    print 'Non repeating'

>>> check_repeat('254725472547')
Repeating on "2547"
>>> check_repeat('abcdeabcdeabcdeabcde')
Repeating on "abcde"

अब जब मैं इसे ध्यान से देखता हूं, तो यह मेरे मूल रूप से पोस्ट किए गए (किसी भी संपादन से पहले) समाधान के समान प्रतीत होता है, जिसमें एकमात्र अंतर लंबाई और प्रतिस्थापन को बचाने वाला होता है। मुझे लगता है कि मैं एक बहुत अच्छा एल्गोरिथ्म था। : पी
टाइगरहॉकट ३

@ TigerhawkT3 हाँ! :)
सकाम वर्मा

9

मैंने इस समस्या के आठ से अधिक समाधानों के साथ शुरुआत की। कुछ रेगेक्स (मैच, फाउंडल, स्प्लिट), कुछ स्ट्रिंग स्लाइसिंग और टेस्टिंग पर आधारित थे, और कुछ स्ट्रिंग मेथड्स (फाइंड, काउंट, स्प्लिट) के साथ थे। प्रत्येक को कोड स्पष्टता, कोड आकार, गति और मेमोरी खपत में लाभ था। मैं अपना उत्तर यहां पोस्ट करने जा रहा था जब मैंने देखा कि निष्पादन की गति को महत्वपूर्ण माना गया था, इसलिए मैंने इस पर पहुंचने के लिए अधिक परीक्षण और सुधार किया:

def repeating(s):
    size = len(s)
    incr = size % 2 + 1
    for n in xrange(1, size//2+1, incr):
        if size % n == 0:
            if s[:n] * (size//n) == s:
                return s[:n]

यह उत्तर कुछ अन्य उत्तरों के समान है, लेकिन इसकी कुछ गति अनुकूलन हैं जिनका दूसरों ने उपयोग नहीं किया है:

  • xrange इस आवेदन में थोड़ा तेज है,
  • यदि एक इनपुट स्ट्रिंग एक विषम लंबाई है, तो किसी भी लम्बाई सबस्ट्रिंग की जाँच न करें,
  • का उपयोग करके s[:n]सीधे , हम प्रत्येक लूप में एक वैरिएबल बनाने से बचते हैं।

मुझे यह देखने में दिलचस्पी होगी कि यह सामान्य हार्डवेयर के साथ मानक परीक्षणों में कैसा प्रदर्शन करता है। मेरा मानना ​​है कि यह अधिकांश परीक्षणों में डेविड झांग के उत्कृष्ट एल्गोरिदम से कम होगा, लेकिन अन्यथा बहुत तेज होना चाहिए।

मुझे यह समस्या बहुत उलटी-सीधी लगी। मैंने सोचा था कि समाधान तेजी से धीमा होगा। धीमे दिखने वाले उपाय तेज थे! ऐसा लगता है कि बहु संचालक और स्ट्रिंग तुलना के साथ पायथन की स्ट्रिंग निर्माण अत्यधिक अनुकूलित है।


बुरा नहीं है सब :-) पर पायथन 3.4 पर बेंचमार्क रन (आंशिक रूप से ओ पी एक संस्करण निर्दिष्ट नहीं है और है कि क्या हर किसी को चाहिए का उपयोग कर, और आंशिक रूप क्योंकि यह नई उपयोग करता हो statisticsमॉड्यूल), तो मैं अपने बदलना पड़ा /को रों //रों और ( 3.x में 2.x की तरह व्यवहार करता है ) के xrange()साथ बदलें । range()xrange()
जीरो पिरियस

यहां बेंचमार्क के संशोधन हैं , इसलिए आप मेरे परिवर्तनों की समीक्षा कर सकते हैं।
शून्य पीरियस

धन्यवाद जीरो। यह तेज़ था। परिणाम मेरी भविष्यवाणियों पर थोड़ा नीचे थे। मुझे संदेह है कि मैंने पायथन 2.7 में गति के लिए जिन तकनीकों का उपयोग किया है वे पायथन 3.4 में बहुत प्रभावी नहीं हैं। ओह, ठीक है - एक मजेदार और शैक्षिक अभ्यास।
लॉजिक नाइट

//3.x में पूर्णांक विभाजन है (सिर्फ 2.x व्यवहार की तरह /), जबकि 3.x का /फ्लोट डिवीजन है (जो मुझे यकीन है कि बहुत धीमा होगा, भले ही इसका उपयोग करने का प्रयास करने से आपका समाधान भंग न हो। सूचकांक के रूप में एक फ्लोट)। जैसा कि उल्लेख किया गया है, 3.x range()2.x के समान ही है xrange(); range()3.x में 2.x का कोई समतुल्य मौजूद नहीं है। इसलिए मुझे नहीं लगता कि बेंचमार्क और आपके द्वारा किए गए किसी भी समय के बीच किसी भी विसंगति का कारण है। यह शायद सिर्फ इतना है कि 3.x 2.x की तुलना में कुल मिलाकर धीमा है (या शायद आपकी मशीन खान से तेज है)।
जीरो पिरियस

जब मुझे कुछ समय मिलता है, तो मुझे पायथन 2 और पायथन 3 के बीच रन-टाइम के अंतर पर करीबी नज़र डालनी होगी।
लॉजिक नाइट

2

यह फ़ंक्शन बहुत तेज़ी से चलता है (परीक्षण किया गया है और यह 100 से अधिक वर्णों के साथ तार पर यहां सबसे तेज़ समाधान की तुलना में 3 गुना अधिक तेज है और अंतर बड़ा हो जाता है जितना अधिक दोहराए जाने वाला पैटर्न होता है)। यह उत्तर पाने के लिए आवश्यक तुलना की संख्या को कम करने की कोशिश करता है:

def repeats(string):
    n = len(string)
    tried = set([])
    best = None
    nums = [i for i in  xrange(2, int(n**0.5) + 1) if n % i == 0]
    nums = [n/i for i in nums if n/i!=i] + list(reversed(nums)) + [1]
    for s in nums:
        if all(t%s for t in tried):
            print 'Trying repeating string of length:', s
            if string[:s]*(n/s)==string:
                best = s
            else:
                tried.add(s)
    if best:
        return string[:best]

ध्यान दें कि स्ट्रिंग 8 की लंबाई के लिए उदाहरण के लिए यह केवल आकार 4 के टुकड़े की जाँच करता है और इसे आगे परीक्षण करने की आवश्यकता नहीं है क्योंकि लंबाई 1 या 2 के पैटर्न का परिणाम लंबाई 4 का पैटर्न दोहराएगा:

>>> repeats('12345678')
Trying repeating string of length: 4
None

# for this one we need only 2 checks 
>>> repeats('1234567812345678')
Trying repeating string of length: 8
Trying repeating string of length: 4
'12345678'

धन्यवाद :) मैंने इसे बहुत अधिक अनुकूलित नहीं किया। मैं सिर्फ अलग दृष्टिकोण प्रस्तुत करना चाहता था क्योंकि अन्य उत्तर पैटर्न खोजने पर ध्यान केंद्रित कर रहे हैं और मेरा दृष्टिकोण यह साबित करने पर ध्यान केंद्रित करता है कि कोई पैटर्न नहीं है :) इसलिए यादृच्छिक तारों के लिए मेरा एल्गोरिथ्म बहुत तेज़ी से चलना चाहिए।
पिओटर डबकोव्स्की

0

डेविड झांग के जवाब में अगर हमारे पास किसी तरह का सर्कुलर बफर होता है तो यह काम नहीं करेगा: principal_period('6210045662100456621004566210045662100456621')शुरू होने के कारण 621, जहां मुझे यह पसंद होगा कि मैं इसे बाहर थूक दूंगा 00456621:।

उनके समाधान का विस्तार हम निम्नलिखित का उपयोग कर सकते हैं:

def principal_period(s):
    for j in range(int(len(s)/2)):
        idx = (s[j:]+s[j:]).find(s[j:], 1, -1)
        if idx != -1:
            # Make sure that the first substring is part of pattern
            if s[:j] == s[j:][:idx][-j:]:
                break

    return None if idx == -1 else s[j:][:idx]

principal_period('6210045662100456621004566210045662100456621')
>>> '00456621'

-1

यहां अजगर में कोड है जो उपयोगकर्ता द्वारा दिए गए मुख्य स्ट्रिंग में उप स्ट्रिंग की पुनरावृत्ति के लिए जांच करता है

print "Enter a string...."
#mainstring = String given by user
mainstring=raw_input(">")
if(mainstring==''):
    print "Invalid string"
    exit()
#charlist = Character list of mainstring
charlist=list(mainstring)
strarr=''
print "Length of your string :",len(mainstring)
for i in range(0,len(mainstring)):
    strarr=strarr+charlist[i]
    splitlist=mainstring.split(strarr)
    count = 0
    for j in splitlist:
        if j =='':
            count+=1
    if count == len(splitlist):
        break
if count == len(splitlist):
    if count == 2:
        print "No repeating Sub-String found in string %r"%(mainstring)

    else:
        print "Sub-String %r repeats in string %r"%(strarr,mainstring)
else :
    print "No repeating Sub-String found in string %r"%(mainstring)

इनपुट :

0045662100456621004566210045662100456621

आउटपुट :

आपकी स्ट्रिंग की लंबाई: 40

उप-स्ट्रिंग '00456621' स्ट्रिंग में दोहराता है '0045662100456621004566210045662100456621'

इनपुट :

004608294930875576036866359447

आउटपुट :

आपकी स्ट्रिंग की लंबाई: 30

स्ट्रिंग में पाया गया कोई दोहराव उप-स्ट्रिंग '004608294930875576036866359447'

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.