Pythonで文字列が繰り返されるかどうかを確認するにはどうすればよいですか?


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文字以上になる可能性があるため、各文字をループしてパターンを作成し、パターンをチェックして残りの文字列をチェックすると、非常に遅くなります。これに数百の文字列を掛けると、直感的な解決策がわかりません。

正規表現を少し調べたところ、探しているものがわかっている場合、または少なくとも探しているパターンの長さがわかっている場合は、正規表現が適しているようです。残念ながら、どちらも知りません。

文字列がそれ自体を繰り返しているかどうかを確認するにはどうすればよいですか?


15
各文字をループしてパターンを作成しようとしてから、パターンと残りの文字列をチェックするのひどく遅いように見えますが、そうですか?
Tim


2
@AvinashRajこれは文字列の一部のみに一致し、完全なものではありません。
ジョン

11
@AvinashRaj OPはすべての可能な解決策について尋ねています。リンクした質問は、正規表現の解答のみを受け入れます。正規表現は、問題を解決することができますがでても構わないくらいの必要以上の時間。たとえば、最適なソリューション(つまり、線形時間)では、テキストのサフィックスツリーを使用します。最長の繰り返し部分文字列を見つけて、長さをいくつかチェックするだけです。
Bakuriu 2015

2
@ TigerhawkT3実際のデータセットは大きすぎて扱いにくいですが、質問の例はその一部です。必要に応じて、さらにいくつか示します
ジョン

回答:


570

正規表現を避け、Python内のループを遅くする簡潔なソリューションを次に示します。

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

ベンチマークの結果については、@ davidismによって開始されたコミュニティWikiの回答を参照してください。要約すれば、

デビッド・チャンのソリューションは明らかな勝者であり、大規模なサンプルセットでは他のすべてのソリューションよりも少なくとも5倍優れています。

(私の答えではなく、その答えの言葉。)

これは、文字列が自明ではない回転に等しい場合にのみ、文字列が周期的であるという観察に基づいています。@AleksiTorhamoに感謝します。これにより、sin の最初の出現のインデックスから主要な期間を回復できることを認識し、Pythonの(s+s)[1:-1]オプションstartend引数について通知しますstring.find


19
これを拡張して.find()、の.index()代わりにin、またはを使用して、最短の繰り返しサブシーケンスを見つけることができます。(s+s).find(s, 1, -1)
Aleksi Torhamo 2015

11
また、スライシングは文字列全体の(ほぼ)別のコピーを作成する必要があるため、少なくともより大きな文字列の(s+s).find(s, 1, -1)場合は(s+s)[1:-1].find(s)、よりも(非常にわずかに)高速になると思います。
Aleksi Torhamo 2015

13
周期関数から正弦波または余弦波を取り、それを右にシフトするようなものです。それは完全に周期的であるので、波は最終的に完全に一致します...この解に相当する数学はただ驚異的です。:)私はあなたに+∞の賛成票を与えられることを望みます。
Shashank 2015

6
GuidoのPEP 8への最近の更新はここで重要です:「returnステートメントで一貫してください。関数内のすべてのreturnステートメントは式を返すか、どれも返さないかのどちらかです。return ステートメントが式を返す場合、値がないすべてのreturnステートメント戻り値は、これをreturn Noneとして明示的に示す必要があり、明示的なreturnステートメントが関数の最後にある必要があります(到達可能な場合)。 "
ゼロピレウス

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+$は3つの部分に分かれています。

  1. (.+?)+?貪欲ではないため)任意の文字を少なくとも1つ(ただし、できるだけ少なく)含む一致グループです。

  2. \1+ 最初の部分で一致するグループが少なくとも1回繰り返されるかどうかをチェックします。

  3. $文字列の終わりをチェックして、繰り返される部分文字列の後に余分な非繰り返しコンテンツre.match()がないことを確認します(これを使用すると、繰り返される部分文字列のに繰り返されないテキストがないことを確認します)。

Python 3.4以降では、をドロップして代わりに$使用re.fullmatch()するか、(少なくともPythonで2.3までさかのぼって)逆の方法でre.search()正規表現を使用でき^(.+?)\1+$ます。これらはすべて、他のものよりも個人的な好みに基づいています。


6
なぜこの簡潔な2ライナーが最高投票の回答ではないのか、私にはわかりません。他の答えは悪くありませんが、これははるかに優れています。(頻繁に不正解される正規表現を使用する可能性がありますが、これは、ネストされたブロック、潜在的なタイプミス、オフバイワンエラーなどでいっぱいの、他のはるかに長い回答よりもはるかに簡単に検査できます。)私は考えます。
Paul Draper

9
その理由は主に2つあると思います。1)一部のプログラマーは正規表現よりも数学が好きです。2)入力文字列の長さと性質を変えると、さまざまな答えがパフォーマンスを向上させます。 (実際のデータには表示されないこともあります)このソリューションは最適ではないように見えます。
TigerhawkT3 2015

時々、あなたは正規表現に対する大きな偏見に遭遇します。Iveには2人のマネージャーがいて、正規表現の使用を禁じているため、正規表現が仕事に不適切なツールであると聞いていました。offcourseを除いて、彼らは私に正規表現エンジンを実装するように頼むことによって進んだ
joojaa

1
@PaulDraper:舞台裏で正規表現が何をしていると思いますか?これは文字列を解析し、再一致が発生する可能性があるまで各要素を格納します。それは、OPが遅すぎると述べたことと同じです。2ライナーだからといって、パフォーマンスの向上はありません。
dhein 2015

2
@ザイビス、私は通常同意しますが、これは最短かつ最速のソリューションです(stackoverflow.com/a/29482936/1212596) .... 私がそのコメントをした後に投稿されたDavidを除いて。私は実際にはデビッドのアプローチがもっと好きです(賢い!)。
Paul Draper

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

編集: Python 3では、/演算子はデフォルトで浮動小数点除算を行うように変更されました。intPython 2から除算を取得するには、//代わりに演算子を使用できます。これを私の注意を喚起してくれた@ TigerhawkT3に感謝します。

//両方のPython 2とPython 3で除算整数オペレータを行うには、私は両方のバージョンをサポートするための答えを更新しました。すべての部分文字列が等しいかどうかを確認するためにテストする部分はall、ジェネレータ式を使用した短絡操作です。

更新:元の質問の変更に応じて、コードが更新され、存在する場合と存在Noneしない場合の最小の繰り返し部分文字列を返すようになりました。@godlygeekはを使用divmodしてdivisorsジェネレーターの反復回数を減らすことを提案しており、コードもそれに合わせて更新されています。現在、すべての正の約数を返しますnn自分自身を除いて、昇順になりました。

高パフォーマンスのための更なる更新:複数のテストの後、文字列の等価性をテストするだけで、Pythonのスライスやイテレーターソリューションの中で最高のパフォーマンスが得られるという結論に達しました。したがって、私は@ TigerhawkT3の本から一枚の葉を取り、私のソリューションを更新しました。今では以前の6倍以上の速さで、特にTigerhawkのソリューションよりも高速ですが、Davidのソリューションよりも低速です。


3
このソリューションは素晴らしいです。除数メソッドを変更して、生産者と消費者のパターンをたどることができます。それらが見つかると結果が得られるようにします。これが最高の答えでない場合は、残念です。それ以外は総当たりです。
JustinDanielson 2015

3
@JustinDanielsonこれは、暗黙的なプロデューサーであるジェネレーター式から作成されたジェネレーターオブジェクトを返します:)除数を遅延評価します。
Shashank 2015

1
おお。知らなかった。ええと、その時はさらに良いです。:DIはsqrtを避けたい理由を理解していますが、除数の範囲の上限としてn / 2を使用できます。
JustinDanielson 2015

1
@JustinDanielson提案のおかげで、範囲の上限が(n/2)包括的になりました。
Shashank 2015

1
必要があるn / 2divisors()あることn // 2
TigerhawkT3 2015

87

この質問に対するさまざまな回答のベンチマークを以下に示します。テスト対象の文字列によってパフォーマンスが大幅に異なるなど、いくつかの驚くべき結果がありました。

一部の関数は、Python 3で動作するように変更されました(主に///除算して整数除算を行うことにより)。何か問題がある場合は、関数を追加するか、別のテスト文字列を追加して、Pythonチャットルームで @ZeroPiraeusにpingを送信します。

要約すると、OPがここでこのコメントを介し)提供する大量のサンプルデータセットに対して、パフォーマンスが最も高いソリューションと最も悪いソリューションの間には約50倍の違いがあります。デビッド・チャンのソリューションは明らかに大きな勝者であり、大規模なサンプルセットでは他のすべてのソリューションを約5倍上回っています。

非常に大きな「一致しない」ケースでは、いくつかの回答が非常に遅くなります。それ以外の場合、テストによっては、関数が同等に一致するか、勝者が明確であるように見えます。

以下は、さまざまな分布を示すためにmatplotlibとseabornを使用して作成されたプロットを含む結果です。


コーパス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

上記のソリューションが元のソリューションよりも数パーセント遅くなることはほとんどありませんが、通常はかなり高速です。それでも、長い文字列の場合はdavidismより速くはありません。ゼロの正規表現ソリューションは短い文字列の場合に優れています。ギブハブでのdavidismのテストによると、約1000〜1500文字の文字列で最速になります。とにかく、私がテストしたすべてのケースで、確実に2番目に高速(またはそれ以上)です。ありがとう、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

これはブルートフォースソリューションではありませんか?
JustinDanielson 2015

7
@JustinDanielsonはい。しかし、それでも解決策はあります。
Sinkingpoint 2015

3
短い文字列の場合は約1e-5〜3e-5秒、成功した長い(1000文字)文字列の場合は3e-5〜4e-5秒、失敗した長い文字列(最悪の場合)はミリ秒未満です。 。したがって、1000文字の文字列は1,000秒程度かかります。数学の答えと比較して、これは10倍速く一致を見つけますが、失敗するまでに3倍時間がかかります。
TigerhawkT3 2015

repeat('aa')リターンNone
Tom Cornebize、2015

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

これは最短一致を返すか、一致がない場合はNoneを返します。


16

O(n)最悪の場合、接頭辞関数を使用して問題を解決することもできます。

注、それが一般的なケースでは、より遅い(UPD:と非常に遅い)であってもよいの約数の数に依存する他のソリューションよりもn、通常見つけ、私は彼らのために悪い例1はなると思い早く失敗aaa....aabがある場合、n - 1 = 2 * 3 * 5 * 7 ... *p_n - 1 aS」

まず、前置関数を計算する必要があります

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 == 0k != 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()有効なPythonではありません:あなたのwhileand ifステートメントに、そしての&&代わりにコロンがありませんand。それらを修正した後UnboundLocalError: local variable 'i' referenced before assignment、行のために失敗しfor i in range(i, n):ます。
Zero Piraeus、

ありがとう:-) prefix_function()他の回答と同様の結果を返すためにを使用する関数をまとめることができる場合–最短の部分文字列またはNone–私がまとめる改訂されたベンチマークにそれを含めます。
Zero Piraeus

@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

TigerhawkT3に感謝します。これがないと、ケースに一致しlength // 2ない+ 1ことに気づきましたabab


このソリューションは、実際には私の最適化されたソリューションとほぼ同じです。私がしたように、あなたにはのrange制限があることがわかりますlength//2- length//2+1正確に2倍にされた文字列(たとえば'aabaab')をキャッチしたい場合は、に変更する必要があります。
TigerhawkT3 2015

そして今、それらは同じです!\ o /今後、最適化にもっと注意を払う必要がありますが、アルゴリズム自体が適切であったことをうれしく思います。
TigerhawkT3 2015

15

正規表現なしの簡単な解決策を次に示します。

s長さが1〜の、0番目のインデックスから始まる部分文字列の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"

これを注意深く見ると、最初に投稿された(編集前の)ソリューションとほとんど同じであるように見えますが、長さと部分文字列が保存されている点だけが異なります。私はかなり良いアルゴリズムを持っていたと思います。:P
TigerhawkT3 2015

@ TigerhawkT3そうですね!:)
Saksham Varma 2015

9

私はこの問題の8つ以上の解決策から始めました。一部は正規表現(match、findall、split)、一部は文字列のスライスとテスト、一部は文字列メソッド(find、count、split)に基づいていました。それぞれに、コードの明確さ、コードサイズ、速度、メモリ消費の点で利点がありました。実行速度が重要としてランク付けされていることに気付いたとき、私はここに私の答えを投稿するつもりだったので、これに到達するためにより多くのテストと改善を行いました:

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]直接使用することで、各ループで変数を作成することを避けます。

一般的なハードウェアを使用した標準テストでこれがどのように機能するかを知りたいと思います。ほとんどのテストでは、David Zhangの優れたアルゴリズムには十分足りないと思いますが、それ以外の場合はかなり高速になるはずです。

この問題は直感に反していることがわかりました。高速だと思った解決策は遅かった。遅く見えるソリューションは高速でした!乗算演算子を使用したPythonの文字列作成と文字列比較は非常に最適化されているようです。


すべての悪くない:-)のPython 3.4のベンチマークを実行(OPバージョンを指定していない一因と誰もが何がすべきことは、新たな使用使用され、そして部分的にので、statistics私はあなたの変更しなければならなかったので、モジュール)/に秒//秒そして、交換するxrange()range()(これは2.xの者のように振る舞うxrange()3.xでは)。
ゼロピレウス

ベンチマークの改訂版は次のとおりです。ちなみに私の変更を確認できます。
ゼロピレウス

ありがとう、ゼロ。それは速かった。結果は私の予測よりわずかに下がっていました。私がPython 2.7で高速化に使用した手法は、Python 3.4ではあまり効果的ではないと思います。ああ、まあ-楽しく教育的な演習。
ロジックナイト

//3.xは整数除算です(2.xの動作と同様/)。3.x /は浮動小数点除算です(使用を試みてソリューションを壊さなかったとしても、かなり遅くなるはずです)インデックスとしてのフロート)。前述のように、3.x range()は2.xと同じものxrange()です。range()3.xには2.xに相当するものはありません。だから、それがベンチマークとあなたが作ったタイミングとの間の不一致の原因ではないと思います。それはおそらく、3.xが2.xよりも全体的に遅いということです(または、マシンが私のマシンより速いかもしれません)。
Zero Piraeus、

私はいくつかの時間を得るとき、私は、Python 2とPython 3の間の実行時の違いをよく見て持っているもの
ロジック騎士

2

この関数は非常に高速に実行されます(テスト済みであり、10万文字を超える文字列では、最速のソリューションよりも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'

ありがとう:)私はそれをあまり最適化しませんでした。他の答えはパターンを見つけることに焦点を当てており、私のアプローチはパターンがないことを証明することに焦点を当てているので、私は異なるアプローチを提示したかっただけです:)したがって、ランダム文字列の場合、アルゴリズムははるかに速く実行されます。
Piotr Dabkowski、2015

0

David Zhangの回答では、ある種の循環バッファがある場合、これは機能しません。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

これは、ユーザーが指定したメイン文字列内のサブ文字列の繰り返しをチェックするpythonのコードです。

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」で繰り返されます

入力

004608294930875576​​036866359447

出力

文字列の長さ:30

文字列 '004608294930875576​​036866359447'に繰り返しサブ文字列が見つかりません

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.