如何在给定数组中找到重复的字符序列?

时间:2010-09-09 09:36:43

标签: arrays language-agnostic puzzle

我的问题是找到给定数组中重复的字符序列。简单地说,识别字符出现的模式。

   .---.---.---.---.---.---.---.---.---.---.---.---.---.---.
1: | J | A | M | E | S | O | N | J | A | M | E | S | O | N |
   '---'---'---'---'---'---'---'---'---'---'---'---'---'---'

   .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
2: | R | O | N | R | O | N | R | O | N | R | O | N | R | O | N |
   '---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'

   .---.---.---.---.---.---.---.---.---.---.---.---.
3: | S | H | A | M | I | L | S | H | A | M | I | L |
   '---'---'---'---'---'---'---'---'---'---'---'---'

   .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.
4: | C | A | R | P | E | N | T | E | R | C | A | R | P | E | N | T | E | R |
   '---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'


实施例

鉴于以前的数据,结果应为:

  1. "JAMESON"
  2. "RON"
  3. "SHAMIL"
  4. "CARPENTER"

  5. 问题

    • 如何有效地处理这个问题?

14 个答案:

答案 0 :(得分:25)

Tong-in-cheek O(NlogN)解决方案

对字符串执行FFT(将字符视为数值)。结果图中的每个峰对应于子串周期。

答案 1 :(得分:18)

对于您的示例,我的第一种方法是

  1. 获取数组的第一个字符(对于您的上一个示例,即C
  2. 获取数组中该字符的下一个外观的索引(例如9)
  3. 如果找到,则在角色的两个外观之间搜索子串的下一个外观(在本例中为CARPENTER
  4. 如果找到了,你就完成了(结果就是这个子字符串)。
  5. 当然,这仅适用于可能数组的非常有限的子集,其中相同的单词一次又一次地重复,从头开始,中间没有杂散字符,并且其第一个字符不在单词内重复。但是你所有的例子都属于这个类别 - 我更喜欢最简单的解决方案,它可能有用: - )

    如果重复的单词多次包含第一个字符(例如CACTUS),则可以扩展算法以查找该字符的后续出现,而不仅仅是第一个出现(以便它找到整个重复的字符)单词,不仅是它的子串。)

    请注意,此扩展算法会为您的第二个示例提供不同的结果,即RONRON而不是RON

答案 2 :(得分:6)

在Python中,您可以利用正则表达式:

def recurrence(text):
    import re
    for i in range(1, len(text)/2 + 1):
        m = re.match(r'^(.{%d})\1+$'%i, text)
        if m: return m.group(1)

recurrence('abcabc') # Returns 'abc'

我不确定这会如何转化为Java或C.(这是我喜欢Python的原因之一,我猜。: - )

答案 3 :(得分:2)

首先编写一个方法,在容器字符串中找到重复的子串sub,如下所示。

boolean findSubRepeating(String sub, String container);

现在继续通过增加容器中的子字符串来调用此方法,首先尝试1个字符子字符串,然后尝试2个字符等,直到container.length/2

答案 4 :(得分:1)

伪代码

len = str.length
for (i in 1..len) {
   if (len%i==0) {
      if (str==str.substr(0,i).repeat(len/i)) {
         return str.substr(0,i)
      }
   }
}

注意:为了简洁起见,我正在为字符串发明一个“重复”方法,这实际上并不是Java字符串的一部分; “ABC” .repeat(2)= “ABCABC”

答案 5 :(得分:1)

使用C ++:

//Splits the string into the fragments of given size
//Returns the set of of splitted strings avaialble
set<string> split(string s, int frag)
{
    set<string> uni;
    int len = s.length();
    for(int i = 0; i < len; i+= frag)
    {
        uni.insert(s.substr(i, frag));
    }

    return uni;
}

int main()
{

    string out;
    string s = "carpentercarpenter";
    int len = s.length();

      //Optimistic approach..hope there are only 2 repeated strings
      //If that fails, then try to break the strings with lesser number of
      //characters
    for(int i = len/2; i>1;--i)
    {
        set<string> uni = split(s,i);
        if(uni.size() == 1)
        {
            out = *uni.begin();
            break;
        }
    }

    cout<<out;
    return 0;

}

答案 6 :(得分:1)

我想到的第一个想法是尝试所有重复长度(长度(S)= N)的重复序列。最多有N / 2个这样的长度,因此这会导致O(N ^ 2)算法

但我确信它可以改善......

答案 7 :(得分:0)

这是一个具体的工作示例:

/* find greatest repeated substring */
char *fgrs(const char *s,size_t *l)
{
  char *r=0,*a=s;
  *l=0;
  while( *a )
  {
    char *e=strrchr(a+1,*a);
    if( !e )
      break;
    do {
      size_t t=1;
      for(;&a[t]!=e && a[t]==e[t];++t);
      if( t>*l )
        *l=t,r=a;
      while( --e!=a && *e!=*a );
    } while( e!=a && *e==*a );
    ++a;
  }
  return r;
}

  size_t t;
  const char *p;
  p=fgrs("BARBARABARBARABARBARA",&t);
  while( t-- ) putchar(*p++);
  p=fgrs("0123456789",&t);
  while( t-- ) putchar(*p++);
  p=fgrs("1111",&t);
  while( t-- ) putchar(*p++);
  p=fgrs("11111",&t);
  while( t-- ) putchar(*p++);

答案 8 :(得分:0)

我将数组转换为String对象并使用正则表达式

答案 9 :(得分:0)

不确定如何定义“有效”。为了方便/快速实现,您可以在Java中执行此操作:

    private static String findSequence(String text) {
        Pattern pattern = Pattern.compile("(.+?)\\1+");
        Matcher matcher = pattern.matcher(text);
        return matcher.matches() ? matcher.group(1) : null;
    }

它试图找到必须至少重复一次(.+?)的最短字符串(\1+)以匹配整个输入文本。

答案 10 :(得分:0)

将所有角色放入数组e.x.一个[]

i=0; j=0;
for( 0 < i < count ) 
{
if (a[i] == a[i+j+1])
    {++i;}
else
    {++j;i=0;}
}

然后(i / j)的比率=数组中的重复计数。 您必须注意ij的限制,但这是一个简单的解决方案。

答案 11 :(得分:0)

这是一个更普遍的问题解决方案,它会在一个序列(任何东西)中找到重复的子序列,其中子序列不必从头开始,也不必紧接着相互跟随。

给定序列b [0..n],包含有问题的数据,阈值t是要查找的最小子序列长度,

l_max = 0, i_max = 0, j_max = 0;
for (i=0; i<n-(t*2);i++) {
  for (j=i+t;j<n-t; j++) {
    l=0;
    while (i+l<j && j+l<n && b[i+l] == b[j+l])
      l++;
    if (l>t) {
      print "Sequence of length " + l + " found at " + i + " and " + j);
      if (l>l_max) {
        l_max = l;
        i_max = i;
        j_max = j;
      }
    }
  }
}
if (l_max>t) {
  print "longest common subsequence found at " + i_max + " and " + j_max + " (" + l_max + " long)";
}

基本上:

  1. 从数据的开头开始,迭代直到结束的2 * t内(没有可能的方法在长度小于2 * t的空间中有两个不同的长度为t的子序列!)
  2. 对于第二个子序列,从第一个序列开始的位置开始至少t个字节。
  3. 然后,将发现的子序列的长度重置为0,并检查i + l和j + l是否有共同字符。只要你这样做,增加l。 当你不再拥有一个共同的角色时,你已经达到了共同的子序列。 如果子序列超过阈值,则打印结果。

答案 12 :(得分:0)

我自己想出来并为此写了一些代码(用C#编写)并附带了很多评论。希望这有助于某人:

// Check whether the string contains a repeating sequence.
public static bool ContainsRepeatingSequence(string str)
{
    if (string.IsNullOrEmpty(str)) return false;

    for (int i=0; i<str.Length; i++)
    {
        // Every iteration, cut down the string from i to the end.
        string toCheck = str.Substring(i);

        // Set N equal to half the length of the substring. At most, we have to compare half the string to half the string. If the string length is odd, the last character will not be checked against, but it will be checked in the next iteration.
        int N = toCheck.Length / 2;

        // Check strings of all lengths from 1 to N against the subsequent string of length 1 to N.
        for (int j=1; j<=N; j++)
        {
            // Check from beginning to j-1, compare against j to j+j.
            if (toCheck.Substring(0, j) == toCheck.Substring(j, j)) return true;
        }
    }

    return false;
}

如果不清楚其原因,请随时提出任何问题。

答案 13 :(得分: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;
}