如何确定周期序列的最小周期

时间:2019-06-17 07:45:06

标签: python string algorithm

我正在进行文本挖掘,并尝试清理弹幕(弹幕)数据。(子弹幕是视频网站中的一种注释)我的数据中有很多重复的表达式。 (“ LOL LOL LOL”,“ LMAOLMAOLMAOLMAO”。)我想获得“ LOL”,“ LMAO”。

在大多数情况下,我想找到序列的最小周期。

转角情况:输入序列的尾部可以看作是周期性子序列的一部分。

"eat an apple eat an apple eat an" # input
"eat an apple" # output

还有其他一些测试用例:

cases = [
    "abcd",        #4  abcd
    "ababab",      #2  ab
    "ababcababc",  #5  ababc
    "abcdabcdabc", #4  abcd
]

注意:至于最后一种情况“ abcdabcdabc”,“ abcd”优于“ abcdabcdabc”,因为最后三个字符“ abc”是“ abcd”的一部分。

def solve(x):
    n = len(x)
    d = dict()
    T = 0
    k = 0
    while k < n:
        w = x[k]
        if w not in d:
            d[w] = T
            T += 1
        else:
            while k < n and d.get(x[k], None) == k%T:
                k += 1
            if k < n:
                T = k+1
        k += 1
    return T, x[:T]

对于前两种情况,它可以输出正确的答案,但无法全部解决。

3 个答案:

答案 0 :(得分:1)

我不太懂Python,但可以轻松描述所需的算法:

found <- false
length <- inputString.length
size = 1
output <- inputString
while (not found) and (size <= length / 2) do
    if (length % size = 0) then
        chunk <- inputString.substring(0, size)
        found <- true
        for (j <- 1,length/size) do
            if (not inputString.substring(j * size, size).equals(chunk)) then
                found <- false
            if end
        for end
        if found then
            output <- chunk
        if end
    if end
    size <- size + 1
while end

这个想法是越来越多地从字符串的开头开始获取子字符串,子字符串的开始长度为1,并且当您找不到重复的循环时,您增加了长度(直到显然不再可行,是,已达到输入长度的一半)。在每次迭代中,您都将子字符串的长度与输入字符串的长度进行比较,如果输入字符串的长度不能与当前子字符串整除,则当前子字符串对于输入字符串将不会重复(优化将是以找出输入字符串的长度可除以什么数字,并仅检查子字符串中的该长度,但是为了易于理解,我避免了这种优化)。如果字符串的大小可被当前大小整除,则从输入字符串的开始直到当前大小取子字符串,然后检查是否重复。第一次找到这种模式时,就可以停止循环,因为已经找到了解决方案。如果找不到这样的解决方案,则输入字符串是最小的重复子字符串,它会重复0次,因为在您的字符串中只会发现一次。

编辑

如果您想容忍最后一次出现只是模式的一部分,并受inputString的限制,则可以像这样更改算法:

found <- false
length <- inputString.length
size = 1
output <- inputString
while (not found) and (size <= length / 2) do
    chunk <- inputString.substring(0, size)
    found <- true
    for (j <- 1,length/size) do
        if (not inputString.substring(j * size, size).equals(chunk)) then
            found <- (chunk.indexOf(inputString.substring(j).length) = 0)
        if end
    for end
    if found then
        output <- chunk
    if end
    size <- size + 1
while end

在这种情况下,我们看到的行

            found <- (chunk.indexOf(inputString.substring(j).length) = 0)

因此,在不匹配的情况下,我们检查块是否以字符串的其余部分开头。如果是这样,那么我们就在输入字符串的末尾,并且模式会部分匹配直到字符串的末尾,因此发现将为真。如果不是,则发现将为假。

答案 1 :(得分:1)

有效的Z-algorithm

  

给定一个长度为n的字符串S,Z算法产生一个数组Z   其中Z [i]是从S [i]开始的最长子串的长度   也是S的前缀,即最大k   S [j] = S [i + j]对于所有0≤j

计算字符串的Z数组,并找到具有属性ii + Z[i] == lenlen % i == 0是字符串长度)的位置len。现在i是周期长度

答案 2 :(得分:0)

您可以这样操作:

def solve(string):
    foundPeriods = {}

    for x in range(len(string)):
        #Tested substring
        substring = string[0:len(string)-x]
        #Frequency count
        occurence_count = string.count(substring)

        #Make a comparaison to original string
        if substring  * occurence_count in string:
            foundPeriods[occurence_count] = substring 

    return foundPeriods[max(foundPeriods.keys())]


for x in cases:
    print(x ,'===> ' , solve(x), "#" , len(solve(x)))
    print()

输出

abcd ===>  a # 1
ababab ===>  ab # 2
ababcababc ===>  ababc # 5
abcdabcdabc ===>  abcd # 4

编辑: 编辑答案以考虑问题中的以下内容

  

“ abcdabcdabc”,“ abcd”比“ abcdabcdabc”更好,因为它更自然地出现