是否有快速算法来删除字符串中重复的子串?

时间:2017-04-28 09:19:55

标签: python string algorithm

有一个类似的字符串

dxabcabcyyyydxycxcxz

我希望将其合并到

dxabcydxycxz

其他例子:   ddxddx - > dxdx,abbab - > abab。

规则是:

if (adjacent and same): merge

# Such as 'abc',they are same and , so I will delete one of them .
# Although 'dx' is same as 'dx',they are nonadjacent,so I do not delete any of them
# If one character has been deleted, we don't delete any sub-string include it 

我是在python的代码中完成的,但是在长字符串中它的速度很慢。

# original string
mystr = "dxabcabcyyyydxycxcxz"
str_len = len(mystr)
vis = [1] *str_len #Use a list to mark which char is deleted

# enumerate the size of sub-str
for i in range(1,str_len):
    # enumerate the begin of the sub-str
    for j in range(0, str_len):
        offset = 2 #the size of sub-str + 1
        current_sub_str = mystr[j:j+i]
        s_begin = j+i*(offset-1)
        s_end = j+(i*offset)
        # delete all of the same char
        while((j+(i*offset) <= str_len) and current_sub_str == mystr[s_begin:s_end]
              and 0  not in vis[s_begin:s_end] and 0  not in vis[j:j+i]):
            vis[s_begin:s_end] = [0] * (s_end - s_begin) #if I deleted it ,mark it as 0
            offset += 1
            s_begin = j + i * (offset - 1)
            s_end = j + (i * offset)

res = []
for i in range(0,str_len):
    if(vis[i]!=0): res.append(mystr[i])

print "".join(res)

有没有更快的方法来解决它?

  

2017年4月29日更新

抱歉,这似乎是一个XY问题。另一方面,它可能不是。 有内容

我正在编写一个网络蜘蛛编码,并且有很多标记路径就像那些

ul/li/a
ul/li/div/div/div/a/span
ul/li/div/div/div/a/span 
ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a
ul/li/ul/li/a

如您所见,有一些标记路径&#39;我也是这样做的,所以我想把它们折叠起来,发现其他任何标签路径都有相同的结构。 折叠后,我得到了标记路径&#39;像这样。

ul/li/a
ul/li/div/div/div/a/span
ul/li/a
ul/li/ul/li/a
ul/li/a
ul/li/ul/li/a
ul/li/a
ul/li/ul/li/a

这只是我的想法,我不知道这样做是否合适。(尝试之后,我选择了另一种方式来做。

然而,有一个有趣的问题,如ACM问题。

所以我简化了一个标记路径&#39;对于一个角色并寻求帮助。因为我自己没有快速行动。 实际上,这个问题有许多我不介意的角落,并感谢大家帮助我完成它。

谢谢大家。

6 个答案:

答案 0 :(得分:15)

看到正则表达的力量:

>>> import re

>>> re.sub(r"(.+?)\1+", r"\1", "dxabcabcyyyydxycxcxz")
'dxabcydxycxz'

>>> re.sub(r"(.+?)\1+", r"\1", "ddxddx")
'dxdx'

>>> re.sub(r"(.+?)\1+", r"\1", "abbab")
'abab'

这会查找包含1个或多个任意字符(.+?)的序列(作为非贪婪匹配,以便首先尝试更短的序列),然后重复匹配序列{{1}的1次或多次重复},并将其全部替换为匹配的序列\1+

答案 1 :(得分:0)

这可以是一个开始:

for i in range(len(string)):
    for j in range(i + 1, len(string)):
        while string[i:j] == string[j:j + j - i]:
            string = string[:j] + string[j + j - i:]

提供的示例的结果:

abbab  -> abab
ddxddx -> dxdx
abcabcabc -> abc
dxabcabcyyyydxycxcxz -> dxabcydxycxz

答案 2 :(得分:0)

这是一个很好的问题/系列回复!

这是使用生成器和字符串切片的实现:

import math
def dedupe(string, step=1):
    index = 0
    prior = ''
    while index < len(string):
        letter = string[index]
        window = index + step
        comparison = string[index:window]
        if comparison != prior:
            yield letter
            prior += letter
            index += 1
        else:
            index += step
        if len(prior) > (step):
            prior = prior[1:] # remove first character


def collapse(string):
    step = 1
    while step < math.sqrt(len(string)):
        generator = dedupe(string, step=step)
        string = ''.join(generator)
        step +=1
    return string

编辑:更改了步骤搜索以使用长度的平方根来改善搜索时间:

  • %timeit collapse('dxabcabcyyyydxycxcxz') 10000循环,最佳3:每循环24.7μs
  • %timeit collapse(randomword(100) 1000循环,最佳3:每循环384μs
  • %timeit collapse("a" * 100) 10000个循环,最佳3:每循环27.1μs
  • %timeit collapse(randomword(50) * 2) 1000循环,最佳3:每循环382μs

答案 3 :(得分:0)

一行:

def remove_repeats(iterable):
    return [e for (i, e) in enumerate(iterable) if i == 0 or e != iterable[i - 1]]

它适用于任何可迭代数据,返回列表。

>>> print remove_repeats("aaabbc")
['a', 'b', 'c']

>>> s = '''
... ul/li/a
... ul/li/div/div/div/a/span
... ul/li/div/div/div/a/span
... ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... ul/li/ul/li/a
... '''

>>> print remove_repeats(s.split())
['ul/li/a', 'ul/li/div/div/div/a/span', 'ul/li/a', 'ul/li/ul/li/a', 'ul/li/a', '
ul/li/ul/li/a', 'ul/li/a', 'ul/li/ul/li/a']

如果您需要字符串,请加入:

>>> print "".join(remove_repeats('111222333'))
123

>>> print "\n".join(remove_repeats(s.split()))
ul/li/a
ul/li/div/div/div/a/span
ul/li/a
ul/li/ul/li/a
ul/li/a
ul/li/ul/li/a
ul/li/a
ul/li/ul/li/a

答案 4 :(得分:0)

from collections import OrderedDict
mystr = "dxabcabcyyyydxycxcxz"
index=0;indexs = [];count = OrderedDict()
while count!=None:
    count = {}
    for index in range(0,len(mystr)):
        flag = True
        for index1 in range(0,index+1)[::-1]:
            if(mystr.startswith(mystr[index1:index+1], index+1)):
                if count.get(str(index1),0)<(index+1-index1):
                    count.update({str(index1) : index+1-index1})
    for key in count:
        mystr = mystr[:int(key)]+mystr[int(key)+count[key]:]
    if count=={}:
        count=None
print "Answer:", mystr

答案 5 :(得分:0)

一种线性方法

import itertools
_str = 'dxabcabcyyyydxycxcxz'
print ''.join(ch for ch, _ in itertools.groupby(_str))

结果:

dxabcabcyyyydxycxcxz - &gt; dxabcabcydxycxcxz