在单词中找到最短的重复循环?

时间:2011-05-16 17:50:01

标签: algorithm language-agnostic pseudocode

我准备写一个函数,它会给我一个最短的一组字母,这些字母最终会创建给定的单词。

例如,单词 abkebabkebabkeb 由重复的 abkeb 字词创建。我想知道,如何有效地分析输入词,以获得最短的字符创建输入词。

13 个答案:

答案 0 :(得分:12)

这是一个正确的O(n)算法。第一个for循环是KMP的表构建部分。有各种证据表明它总是以线性时间运行。

由于这个问题有4个先前的答案,其中没有一个是O(n)和正确的,我对这个解决方案的正确性和运行时都进行了大量测试。

def pattern(inputv):
    if not inputv:
        return inputv

    nxt = [0]*len(inputv)
    for i in range(1, len(nxt)):
        k = nxt[i - 1]
        while True:
            if inputv[i] == inputv[k]:
                nxt[i] = k + 1
                break
            elif k == 0:
                nxt[i] = 0
                break
            else:
                k = nxt[k - 1]

    smallPieceLen = len(inputv) - nxt[-1]
    if len(inputv) % smallPieceLen != 0:
        return inputv

    return inputv[0:smallPieceLen]

答案 1 :(得分:2)

这是PHP的一个例子:

<?php
function getrepeatedstring($string) {
    if (strlen($string)<2) return $string;
    for($i = 1; $i<strlen($string); $i++) {
        if (substr(str_repeat(substr($string, 0, $i),strlen($string)/$i+1), 0, strlen($string))==$string)
            return substr($string, 0, $i);
    }
    return $string;
}
?>

答案 2 :(得分:1)

O(n)溶液。假设必须覆盖整个字符串。关键的观察是我们生成模式并对其进行测试,但如果我们找到不匹配的方式,我们必须包含我们已经测试过的整个字符串,因此我们不必重新保存这些字符。 / p>

def pattern(inputv):
    pattern_end =0
    for j in range(pattern_end+1,len(inputv)):

        pattern_dex = j%(pattern_end+1)
        if(inputv[pattern_dex] != inputv[j]):

            pattern_end = j;
            continue

        if(j == len(inputv)-1):
            print pattern_end
            return inputv[0:pattern_end+1];
    return inputv;

答案 3 :(得分:0)

我相信有一个非常优雅的递归解决方案。许多提议的解决方案解决了字符串以模式的一部分结束的额外复杂性,如abcabca。但我不认为这是被要求的。

我对clojure中问题的简单版本的解决方案:

 (defn find-shortest-repeating [pattern string]
  (if (empty? (str/replace string pattern ""))
   pattern
   (find-shortest-repeating (str pattern (nth string (count pattern))) string)))

(find-shortest-repeating "" "abcabcabc") ;; "abc"

但请注意,最终找不到不完整的模式。

答案 4 :(得分:0)

我找到了一个基于你的帖子的解决方案,可能采取不完整的模式:

(defn find-shortest-repeating [pattern string]
   (if (or (empty? (clojure.string/split string (re-pattern pattern)))
          (empty? (second (clojure.string/split string (re-pattern pattern)))))
    pattern
    (find-shortest-repeating (str pattern (nth string (count pattern))) string)))

答案 5 :(得分:0)

我的解决方案: 我们的想法是从零位置找到一个子串,使其等于相同长度的相邻子串,当找到这样的子串时返回子串。请注意,如果没有找到重复的子字符串,我将打印整个输入字符串。

public static void repeatingSubstring(String input){
    for(int i=0;i<input.length();i++){
        if(i==input.length()-1){
            System.out.println("There is no repetition "+input);
        }
        else if(input.length()%(i+1)==0){
            int size = i+1;
            if(input.substring(0, i+1).equals(input.substring(i+1, i+1+size))){
                System.out.println("The subString which repeats itself is "+input.substring(0, i+1));
                break;
            }
        }
    }
}

答案 6 :(得分:0)

正则表达式解决方案:

步骤1:使用不属于输入字符串的分隔符字符分隔每个字符,包括尾随字符(即~):

(.)
$1~

示例输入:"abkebabkebabkeb"
示例输出:"a~b~k~e~b~a~b~k~e~b~a~b~k~e~b~"

Try it online in Retina.注意:Retina是一种基于Regex的编程语言,旨在快速测试正则表达式并能够在code-golf challenges中成功竞争。

第2步:使用以下正则表达式查找最短的重复子字符串(其中~是我们选择的分隔符):

^(([^~]+~)*?)\1*$
$1

说明:

^(([^~]+~)*?)\1*$
^               $    # Start and end, to match the entire input-string
  ([^~]+~)           # Capture group 1: One or more non-'~' followed by a '~'
 (        *?)        # Capture group 2: Repeated zero or more time optionally
             \1*     # Followed by the first capture group repeated zero or more times

$1                   # Replace the entire input-string with the first capture group match

示例输入:"a~b~k~e~b~a~b~k~e~b~a~b~k~e~b~"
示例输出:"a~b~k~e~b~"

Try it online in Retina.

第3步:再次移除我们的分隔符,以获得我们的预期结果。

~
<empty>

示例输入:"a~b~k~e~b~"
示例输出:"abkeb"

Try it online in Retina.

Here an example implementation in Java.

答案 7 :(得分:0)

这是我使用队列提出的解决方案,它通过了Codeforce中类似问题的所有测试用例。问题编号是745A

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    string s, s1, s2; cin >> s; queue<char> qu; qu.push(s[0]); bool flag = true; int ind = -1;
    s1 = s.substr(0, s.size() / 2);
    s2 = s.substr(s.size() / 2);
    if(s1 == s2)
    {
        for(int i=0; i<s1.size(); i++)
        {
            s += s1[i];
        }
    }
    //cout << s1 << " " << s2 << " " << s << "\n";
    for(int i=1; i<s.size(); i++)
    {
        if(qu.front() == s[i]) {qu.pop();}
        qu.push(s[i]);
    }
    int cycle = qu.size();

    /*queue<char> qu2 = qu; string str = "";
    while(!qu2.empty())
    {
        cout << qu2.front() << " ";
        str += qu2.front();
        qu2.pop();
    }*/


    while(!qu.empty())
    {
        if(s[++ind] != qu.front()) {flag = false; break;}
        qu.pop();
    }
    flag == true ? cout << cycle : cout << s.size();
    return 0;
}

答案 8 :(得分:0)

在python中最简单的一个:

def pattern(self, s):
    ans=(s+s).find(s,1,-1)
    return len(pat) if ans == -1 else ans

答案 9 :(得分:0)

我在面试中能想到的更简单的答案只是一个 O(n^2) 解决方案,它尝试从 0 开始的所有子字符串组合。

int findSmallestUnit(string str){
    for(int i=1;i<str.length();i++){
        int j=0;
        for(;j<str.length();j++){
            if(str[j%i] != str[j]){
                break;
            }
        }
        if(j==str.length()) return str.substr(0,i);
    }
    return str;
}

现在如果有人对 C++ 中这个问题的 O(n) 解决方案感兴趣:

  int findSmallestUnit(string str){
      vector<int> lps(str.length(),0);
      int i=1;
      int len=0;

      while(i<str.length()){
          if(str[i] == str[len]){
              len++;
              lps[i] = len;
              i++;
          }
          else{
              if(len == 0) i++;
              else{
                  len = lps[len-1];
              }
          }
      }
      int n=str.length();
      int x = lps[n-1];
      if(n%(n-x) == 0){
          return str.substr(0,n-x);    
      }
      return str;
  }

以上只是@Buge 在 C++ 中的回答,因为有人在评论中提问。

答案 10 :(得分:-1)

超级延迟回答,但我在接受采访时得到了这个问题,这是我的回答(可能不是最优的,但也适用于奇怪的测试用例)。

private void run(String[] args) throws IOException {
    File file = new File(args[0]);
    BufferedReader buffer = new BufferedReader(new FileReader(file));
    String line;
    while ((line = buffer.readLine()) != null) {
        ArrayList<String> subs = new ArrayList<>();
        String t = line.trim();
        String out = null;
        for (int i = 0; i < t.length(); i++) {
            if (t.substring(0, t.length() - (i + 1)).equals(t.substring(i + 1, t.length()))) {
                subs.add(t.substring(0, t.length() - (i + 1)));
            }
        }
        subs.add(0, t);
        for (int j = subs.size() - 2; j >= 0; j--) {
            String match = subs.get(j);
            int mLength = match.length();
            if (j != 0 && mLength <= t.length() / 2) {
                if (t.substring(mLength, mLength * 2).equals(match)) {
                    out = match;
                    break;
                }
            } else {
                out = match;
            }
        }
        System.out.println(out);
    }
}

测试用例:

abcabcabcabc
bcbcbcbcbcbcbcbcbcbcbcbcbcbc
dddddddddddddddddddd
adcdefg
bcbdbcbcbdbc
hellohell

代码返回:

ABC
BC
d
adcdefg
bcbdbc
hellohell

答案 11 :(得分:-1)

适用于bcbdbcbcbdbc。

等情况
function smallestRepeatingString(sequence){
  var currentRepeat = '';
  var currentRepeatPos = 0;

  for(var i=0, ii=sequence.length; i<ii; i++){
    if(currentRepeat[currentRepeatPos] !== sequence[i]){
      currentRepeatPos = 0;
      // Add next character available to the repeat and reset i so we don't miss any matches inbetween
      currentRepeat = currentRepeat + sequence.slice(currentRepeat.length, currentRepeat.length+1);
      i = currentRepeat.length-1;
    }else{
      currentRepeatPos++;
    }
    if(currentRepeatPos === currentRepeat.length){
      currentRepeatPos = 0;
    }
  }

  // If repeat wasn't reset then we didn't find a full repeat at the end.
  if(currentRepeatPos !== 0){ return sequence; }

  return currentRepeat;
}

答案 12 :(得分:-1)

我提出了一个简单的解决方案,即使使用非常大的琴弦也可以完美无缺地工作 PHP实现:

Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.