Language Independant:检查字符串是否包含某个子字符串的倍数

时间:2016-10-05 05:23:21

标签: regex language-agnostic pattern-matching

我希望通用算法能够找到一个字符串是否包含重复模式,并且该字符串的任何部分都不会出现在重复模式之外。

例如,查看以下示例字符串:

abcabcabc - true
abcabcabcx - false
cucumbercucumber - true
cucumber - false
abaaabaaabaa - true

我查看this answer,它解决了一些案例的问题,但在cucumber示例的情况下会失败。我需要一些适用于所有情况的东西。

4 个答案:

答案 0 :(得分:5)

https://stackoverflow.com/a/2553533/1763356启发的Python解决方案是

s in (s + s)[1:-1]

假设有效实施O(n),这需要str.__contains__时间。

答案 1 :(得分:1)

这似乎是明显的方法:

String s = "abaaabaabaa" ; // string to test

for (int repeating_pattern_length=1; 
     repeating_pattern_length<=s.length/2;
     repeating_pattern_length++)
{  if (modulo(s.length,repeating_pattern_length)==0)
   { // can fit exactly N times
     String proposed_subpattern=s.substring(0,repeating_pattern_length);
     for (nth_instance=2; // don't need to check 1st occurrence
          nth_instance<=s.length/repeating_pattern_length;
          nth_instance++)
     { // check nth occurrence
       if (!proposed_subpattern.equal(
           s.substring((nth_instance-1)*repeating_pattern_length,
                       repeating_pattern_length)
          cycle repeating_pattern_length; // nth occurrence doesn't match
     }
     return true;
   }
}
return false;

[未测试。这是Java,但我不是专业的Java编码器。原谅我的过失]。

这可以说具有复杂度O(s.length)和一个小的常数因子。

可以考虑构建后缀树(也是线性时间),然后检查树是否具有适当的周期。我怀疑上面的算法在实践中非常好。

答案 2 :(得分:1)

由于您没有要求使用特定语言,因此我建议您查看Repeating String的Rosetta代码页。您可以找到并研究解决该问题的一系列算法。 虽然Rosetta Code中的问题是1s和0s,但大多数解决方案应该适用于任何可能的字符串。

我已经编写了一个通用的Common Lisp递归解决方案,这里是注释代码:

(ql:quickload :alexandria)
(defun rep-stringv (a-str &optional (max-rotation (floor (/ (length a-str) 2))))
  ;; Exit condition if no repetition found.
  (cond ((< max-rotation 1) "Not a repeating string")
        ;; Two checks:
        ;; 1. Truncated string must be equal to rotation by repetion size.
        ;; 2. Remaining chars (rest-str) are identical to starting chars (beg-str)
        ((let* ((trunc (* max-rotation (truncate (length a-str) max-rotation)))
                (truncated-str (subseq a-str 0 trunc))
                (rest-str (subseq a-str trunc))
                (beg-str (subseq a-str 0 (rem (length a-str) max-rotation))))
           (and (string= beg-str rest-str)
                (string= (alexandria:rotate (copy-seq truncated-str) max-rotation)
                         truncated-str)))
         ;; If both checks pass, return the repeting string.
         (subseq a-str 0 max-rotation))
        ;; Recurse function reducing length of rotation.
        (t (rep-stringv a-str (1- max-rotation)))))

测试:

CL-USER> (rep-stringv "cucumber")
"Not a repeating string"
CL-USER> (rep-stringv "abaaabaaabaa")
"abaa"

对于字符串,可以使用suffix tree来实现最佳解决方案,就像您现在可能已经实现的那样 - 因为它是所有地方描述的常见问题,例如Wikipedia

实施它对我来说似乎有点过头了,除非你真的需要表现。在任何情况下,都可以找到后缀树(多种语言)的示例here

答案 3 :(得分:1)

这是一些完成这项工作的基本C ++代码:

<?php
$single="<div id='quote'>

</br><input onclick='this.select();' id='selectable' value='User
'></br><div class='content'>
^^^^^ MILAN TODAY ^^^^^



4
-4-
4444
44444
444444
4444444
44444444
444444444
*3333333333*
444444444
44444444
4444444
444444
44444
4444
444
44
4
*
# 670 # ((( 30 ))) # 370 #

# 789 # ((( 40 ))) # 370 #

***gl***
</br></div></div>";

if (preg_match('/(\R4{3,4}\D44\D)/', $single, $double)) {
        echo "working";
}

?>

它利用了子串长度必须是总字符串长度的除数这一事实。

最糟糕的时间复杂性非常糟糕,类似O(n ^ 2 log(log(n))),但我不确定。 (最坏的情况是字符串恰好包含两个相同的子字符串。)我仍然相信平均来说它应该表现得很好,因为大多数外部循环体只对字符串长度的除数执行而内部循环很快就会中止发现不匹配。

编辑:@Veedrac的解决方案不仅更优雅,而且在大多数情况下也更具性能。为了直接比较,这是C ++版本:

bool IsRepeating( std::string in ) {

    int totalLength = in.length();
    for (int subLength = 1; subLength <= totalLength / 2; subLength++ ) {
        if (totalLength % subLength != 0) continue;

        for (int startPos = 0; startPos < subLength; startPos++) {
            char startChar =in[startPos];
            bool mismatchFound = false;
            for (int delta = subLength; delta < totalLength-startPos; delta += subLength) {
                if (in[startPos+delta] != startChar ) {
                    mismatchFound = true;
                    break;
                }
            }
            if (mismatchFound) {
                break;
            }
            return true;
        }
    }
    return false;
}
然而,它会使用更多内存。如果您不了解该功能的目的,可能很难弄明白。但这也适用于我的原始版本。