如何判断一个字符串是不是在 Python 中重复?
Posted
技术标签:
【中文标题】如何判断一个字符串是不是在 Python 中重复?【英文标题】:How can I tell if a string repeats itself in Python?如何判断一个字符串是否在 Python 中重复? 【发布时间】:2015-06-11 10:19:17 【问题描述】:我正在寻找一种方法来测试给定字符串是否在整个字符串中重复。
例子:
[
'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 个或更多字符,因此循环遍历每个字符以尝试构建模式,然后检查模式与字符串的其余部分似乎慢得可怕。将其乘以可能有数百个字符串,我看不到任何直观的解决方案。
我已经对正则表达式进行了一些研究,当您知道要查找的内容或至少要查找的模式的长度时,它们似乎很有用。不幸的是,我都不知道。
如何判断一个字符串是否在重复,如果是,最短的重复子序列是什么?
【问题讨论】:
遍历每个字符以尝试构建一个模式,然后检查该模式与字符串的其余部分似乎非常慢 - 但真的吗? Writing a regex to detect repeat-characters的可能重复 @AvinashRaj 这只是匹配字符串的一部分,而不是全部内容。 @AvinashRaj OP 正在询问所有可能的解决方案。您链接到的问题仅接受 regex 解决方案。请注意,正则表达式可能能够解决问题,但需要更多 的时间。例如,最佳解决方案(即线性时间)将使用文本的后缀树。您只需要找到最长的重复子字符串并检查长度即可。 @TigerhawkT3 真实数据集太大且笨重,但问题中的示例是其中的一部分,如果您愿意,here's some more。 【参考方案1】:如果你只想知道一个字符串是否在另一个字符串中重复,那么这是最好的(在我看来)和最短的 sn-p:
def rep(repeat,fullstring):
return len(fullstring.split(repeat)) > 2
#rep('test', 'test -others-') - no
#rep('hello', 'hello world') - yes
【讨论】:
【参考方案2】:在 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'
【讨论】:
【参考方案3】:你可以观察到一个字符串被认为是重复的,它的长度必须能被它的重复序列的长度整除。鉴于此,这里有一个解决方案,它生成长度从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 中,/
运算符已更改为默认进行浮点除法。要从 Python 2 获取 int
除法,您可以改用 //
运算符。感谢@TigerhawkT3 让我注意到这一点。
//
运算符在 Python 2 和 Python 3 中都执行整数除法,因此我更新了答案以支持这两个版本。我们测试所有子字符串是否相等的部分现在是使用all
和生成器表达式的短路操作。
更新:为了响应原始问题的更改,代码现已更新,如果存在则返回最小的重复子字符串,如果不存在则返回None
。 @godlygeek 建议使用 divmod
来减少 divisors
生成器上的迭代次数,并且代码也已更新以与之匹配。它现在按升序返回 n
的所有正除数,不包括 n
本身。
进一步更新以提高性能:经过多次测试,我得出的结论是,在 Python 中的任何切片或迭代器解决方案中,简单地测试字符串相等性具有最佳性能。因此,我从@TigerhawkT3 的书中吸取了教训并更新了我的解决方案。它现在的速度是以前的 6 倍以上,明显快于 Tigerhawk 的解决方案,但比 David 的慢。
【讨论】:
这个解决方案很棒。您可以更改除数方法以遵循生产-消费者模式。以便它在发现时产生结果。如果这不是最高答案,那将是一种耻辱。其他一切都是蛮力。 @JustinDanielson 它返回一个从生成器表达式创建的生成器对象,它是一个隐式生产者:) 它会惰性求除数。 哦。我不知道。好吧,那就更好了。 :D 我理解你为什么要避免使用 sqrt,但你可以使用 n/2 作为除数范围的上限。 @JustinDanielson 感谢您的建议,范围上限现在为(n/2)
包括在内。
在divisors()
中的n / 2
应该是n // 2
吗?【参考方案4】:
这是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”中重复
输入:
004608294930875576036866359447
输出:
你的字符串长度:30
在字符串“004608294930875576036866359447”中找不到重复的子字符串
【讨论】:
【参考方案5】:这个函数运行得非常快(在超过 100k 个字符的字符串上经过测试,它比最快的解决方案快 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'
【讨论】:
谢谢 :) 虽然我没有优化它。我只是想提出不同的方法,因为其他答案都集中在寻找模式上,而我的方法侧重于证明没有模式:) 因此,对于随机字符串,我的算法应该运行得更快。【参考方案6】:以下是针对此问题的各种答案的一些基准。有一些令人惊讶的结果,包括根据所测试的字符串的不同性能。
修改了一些函数以适用于 Python 3(主要是将 /
替换为 //
以确保整数除法)。如果您发现有问题,想要添加您的功能,或者想要添加另一个测试字符串,请在 Python chatroom 中 ping @ZeroPiraeus。
总而言之:对于由 OP here 提供的大量示例数据(通过 this 评论),最佳和最差解决方案之间的差异约为 50 倍。 David Zhang's solution 是明显的赢家,在大型示例集上的表现比所有其他人高出约 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
语料库 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
语料库 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
测试和原始结果可用here。
【讨论】:
【参考方案7】:这是一个简洁的解决方案,可以避免正则表达式和缓慢的 Python 循环:
def principal_period(s):
i = (s+s).find(s, 1, -1)
return None if i == -1 else s[:i]
请参阅@davidism 发起的Community Wiki answer 以获取基准测试结果。总之,
David Zhang 的解决方案显然是赢家,对于大型示例集,其性能至少比其他所有解决方案高出 5 倍。
(那个答案的话,不是我的。)
这是基于观察到一个字符串是周期性的当且仅当它等于自身的非平凡旋转。感谢@AleksiTorhamo 意识到我们可以从(s+s)[1:-1]
中第一次出现s
的索引中恢复主要周期,并通知我Python 的string.find
的可选start
和end
参数.
【讨论】:
您可以通过使用.find()
或.index()
而不是in
来扩展它以找到最短的重复子序列,例如。 (s+s).find(s, 1, -1)
.
另外,我认为(s+s).find(s, 1, -1)
会(非常稍微)比(s+s)[1:-1].find(s)
快,至少对于较大的字符串,因为切片意味着您必须创建(几乎)整个字符串的另一个副本.
这就像你从periodic function 中取出一个 sin 或 cos 波并将其向右移动。由于它是完全周期性的,因此波浪最终将完美匹配……与该解决方案的数学相似之处简直是惊人的。 :) 我希望我能给你 +∞ 点赞。
Guido 的 recent update 到 PEP 8 与此处相关:“在返回语句中保持一致。函数中的所有返回语句都应该返回一个表达式,或者它们都不应该返回。如果有的话return 语句返回一个表达式, 任何没有返回值的 return 语句都应将其明确声明为 return None,并且 应在函数末尾显示一个明确的 return 语句(如果可访问)。”
@WayneConrad 取一个字符串,比如"abcd"
,弹出右边的字符,然后把它粘回到左边得到"dabc"
。此过程称为将字符串向右旋转 1 个字符。重复n
次以将字符串向右旋转n
个字符。现在观察,如果我们有一个由k
字符组成的字符串,向右旋转k
的任意倍数会使字符串保持不变。字符串的非平凡旋转是其字符数不是字符串长度的倍数。【参考方案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 的使用乘法运算符和字符串比较创建字符串是高度优化的。
【讨论】:
Not bad at all :-) 基准测试在 Python 3.4 上运行(部分原因是 OP 没有指定版本,而这是每个人应该使用的,部分原因是它使用了新的 @ 987654322@ 模块),所以我不得不将您的/
s 更改为 //
s 并将 xrange()
替换为 range()
(其行为类似于 2.x 在 3.x 中的 xrange()
)。
这里是基准测试的revisions,顺便说一下,您可以查看我的更改。
谢谢零。那很快。结果与我的预测略有下降。我怀疑我在 Python 2.7 中用于提高速度的技术在 Python 3.4 中不是很有效。哦,好吧 - 一个有趣且有教育意义的练习。
3.x 中的 //
是整数除法(就像 /
的 2.x 行为一样),而 3.x 的 /
是浮点除法(我敢肯定会是即使它没有通过尝试使用浮点数作为索引来破坏您的解决方案,也会慢得多)。如前所述,3.x 的range()
与2.x 的xrange()
相同; 3.x 中不存在相当于 2.x 的 range()
。因此,我认为这不是基准与您所做的任何时间之间存在任何差异的原因。可能只是 3.x 总体上比 2.x 慢(或者你的机器比我的快)。
当我有时间的时候,我会仔细看看 Python 2 和 Python 3 之间的运行时差异。【参考方案9】:
这个版本只尝试那些作为字符串长度因素的候选序列长度;并使用*
运算符从候选序列构建一个全长字符串:
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 注意到没有 + 1
的 length // 2
将无法匹配 abab
的情况。
【讨论】:
这个解决方案实际上与我优化的解决方案完全相同。我看到您的range
限制为 length//2
,就像我所做的一样 - 如果您想捕获恰好加倍的字符串(例如 'aabaab'
),则必须将其更改为 length//2+1
。
现在它们是相同的! \o/ 以后需要多注意优化,不过算法本身是好的,我很高兴。【参考方案10】:
这是一个使用正则表达式的解决方案。
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+$
分为三部分:
(.+?)
是一个匹配组,包含至少一个(但尽可能少)任意字符(因为+?
is non-greedy)。
\1+
检查第一部分中匹配组的至少一个重复。
$
检查字符串的结尾,以确保在重复的子字符串之后没有额外的、非重复的内容(使用re.match()
确保在 之前没有非重复的文本 重复的子字符串)。
在 Python 3.4 及更高版本中,您可以删除 $
并改用 re.fullmatch()
,或者(在任何 Python 中至少早于 2.3)另辟蹊径,将 re.search()
与正则表达式 @987654338 一起使用@,所有这些都更取决于个人喜好。
【讨论】:
我不知道为什么这个简洁的两个班轮不是投票率最高的答案。其他答案还不错,但这个要好得多。 (它可能会使用经常被贬低的正则表达式,但我可以比其他更长的答案更容易地检查它,这些答案充满了嵌套块、潜在的拼写错误、一个错误等。 ) 嗯,我想越差越好。 我认为这有两个主要原因:1) 一些程序员喜欢数学多于喜欢正则表达式,以及 2) 因为输入字符串的长度和性质的不同使得不同的答案在性能上领先,超长的边缘情况字符串(甚至可能不会出现在真实数据中)使该解决方案显得次优。 有时你会遇到对正则表达式的巨大偏见。我有 2 位经理禁止使用正则表达式,因为他们听说正则表达式不适合这项工作。除了offcourse,他们继续要求我实现一个正则表达式引擎 @PaulDraper:猜猜正则表达式在幕后做什么?它正在解析字符串并存储每个元素,直到发生可能的重复匹配。这与OP所说的太慢是一样的。所以仅仅因为它是一个 2 班轮,就没有任何性能优势。 @Zaibis,我通常会同意,但这是最短和最快的解决方案(大卫的***.com/a/29482936/1212596)....Except,这是在我发表评论后发布的。我实际上更喜欢大卫的方法(聪明!)。【参考方案11】:非正则表达式解决方案:
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(见 cmets):
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 快,而 zero 的正则表达式解决方案对于短字符串来说更胜一筹。它以大约 1000-1500 个字符的字符串出现最快(根据 davidism 在 github 上的测试 - 请参阅他的答案)。无论如何,在我测试的所有情况下,它都是可靠的第二快(或更好)。谢谢,那个怪人。
测试:
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 是的。但仍然是一个解决方案。 我看到短字符串大约需要 1e-5 到 3e-5 秒,成功的长(1000 个字符)字符串大约需要 3e-5 到 4e-5 秒,还有一点点不到一毫秒对于不成功的长字符串(最坏情况)。因此,一千个 1000 个字符的字符串大约需要一秒钟。与数学答案相比,这发现匹配的速度快 10 倍,但失败的时间要长 3 倍。repeat('aa')
返回None
len(string[0:i])
始终等于 i
(至少在这种情况下)。替换这些,并在变量中保存 len(string)
和 string[0:i]
可能会加快速度。此外,IMO 这是一个很好的解决方案,太棒了;)【参考方案12】:
问题也可以在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]
,否则没有答案
您可以查看证明here(俄语,但在线翻译可能会做到这一点)
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:您的while
和if
语句中缺少冒号,并且&&
而不是and
。修复这些后,由于for i in range(i, n):
行,它以UnboundLocalError: local variable 'i' referenced before assignment
失败。
谢谢 :-) 如果您可以组合一个函数,该函数使用您的 prefix_function()
返回与其他答案类似的结果——无论是最短的子字符串还是 None
——我会将它包含在我正在制定修订后的基准。
@ZeroPiraeus,实际上它工作正常,我只是用错误的方式调用它【参考方案13】:
首先,将字符串减半,只要它是“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
如果没有匹配,则返回最短匹配或无。
【讨论】:
【参考方案14】:这是一个直接的解决方案,没有正则表达式。
对于s
的子字符串,从第 0 个索引开始,长度为 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"
【讨论】:
现在我仔细查看了这个,它似乎与我最初发布的(在任何编辑之前)解决方案几乎相同,唯一的区别是保存了长度和子字符串。我想我有一个很好的算法。 :P @TigerhawkT3 确实如此! :)以上是关于如何判断一个字符串是不是在 Python 中重复?的主要内容,如果未能解决你的问题,请参考以下文章
如何检查一个字符串在 python 中是不是有多个特定字符。示例字符串“mood”显然有两个“o”字符[重复]