首页 > 试题广场 >

给定一个字符串,求出其最长的重复子串。

[问答题]
给定一个字符串,求出其最长的重复子串。
推荐

举例: ask not what your country  can do for you ,but what you can do for you country

最长的重复子序列:can do for you

思路:使用后缀数组解决

分析:

1、由于要求最长公共子序列,则需要 找到字符串的所有子序列 ,即通过产生字符串的后缀数组实现。

2、由于要求最长的重复子序列,则需要对所有子序列进行排序,这样可以把 相同的字符串排在一起

3、 比较 相邻字符串 ,找出两个子串中,相同的字符的个数。

注意,对于一个子串,一个与其重复最多的字符串肯定是紧挨着自己的两个字符串。

步骤:

1、对待处理的字符串 产生后缀数组

2、 对后缀数组排序

3、依次 检测相邻两个后缀的公共长度

4、 取出最大 公共长度 的前缀


举例: 输入字符串 banana

1、字符串产生的后缀数组:
a[0]:banana
a[1]:anana
a[2]:nana
a[3]:ana
a[4]:na
a[5]:a

2、对后缀数组进行快速排序,以将后缀相近的(变位词)子串集中在一起

a[0]:a
a[1]:ana
a[2]:anana
a[3]:banana
a[4]:na
a[5]:nana

之后可以依次检测相邻两个后缀的公共长度并取出最大公共的前缀

代码:

  1. /*给定出一个字符串,输出最长的重复子字符串*/
  2. #include <iostream>
  3. #include <algorithm>
  4. #include <string>
  5. using namespace std;
  6. const int MaxCharNum = 5000000;
  7. bool StrCmp(char* str1,char* str2);
  8. void GenSuffixArray(char* str,char* suffixStr[]);
  9. int ComStrLen(char* str1,char* str2);
  10. void GenMaxReStr(char* str);
  11. int main()
  12. {
  13. char str[MaxCharNum];
  14. cin.getline(str,MaxCharNum);//遇到回车结束
  15. GenMaxReStr(str);
  16. system("pause");
  17. return 1;
  18. }
  19. void GenMaxReStr(char* str)
  20. {
  21. int len = strlen(str);
  22. int comReStrLen = 0;
  23. int maxLoc = 0;
  24. int maxLen = 0;
  25. char* suffixStr[MaxCharNum];
  26. GenSuffixArray(str,suffixStr);//产生后缀数组
  27. //对后缀数组进行排序
  28. sort(suffixStr,suffixStr+len,StrCmp);
  29. //统计相邻单词中相同的字符数,并输出结果
  30. for (int i = 0;i < len-1;i++ )
  31. {
  32. comReStrLen = ComStrLen(suffixStr[i],suffixStr[i+1]);
  33. if (comReStrLen > maxLen)
  34. {
  35. maxLoc = i;
  36. maxLen = comReStrLen;
  37. }
  38. }
  39. //输出结果
  40. for (int i = 0;i < maxLen;i++)
  41. {
  42. cout<<suffixStr[maxLoc][i];
  43. }
  44. cout<<endl;
  45. }
  46. /*为字符串产生其后缀数组,并存放到数组suffixStr中*/
  47. void GenSuffixArray(char* str,char* suffixStr[])
  48. {
  49. int len = strlen(str);
  50. for (int i = 0;i < len;i++)
  51. {
  52. suffixStr[i] = &str[i];
  53. }
  54. }
  55. /*返回str1和str2的共同前缀的长度*/
  56. int ComStrLen(char* str1,char* str2)
  57. {
  58. int comLen = 0;
  59. while(*str1 && *str2)
  60. {
  61. if (*str1 == *str2)
  62. {
  63. comLen++;
  64. }
  65. str1++;
  66. str2++;
  67. }
  68. return comLen;
  69. }
  70. //字符串升序排序
  71. bool StrCmp(char* str1,char* str2)
  72. {
  73. if (strcmp(str1,str2) >=0 )
  74. {
  75. return false;
  76. }
  77. return true;
  78. }

程序输入:ask not what your country can do for you,but what you can do for your country

输出:can do for you

时间复杂度分析:产生后缀数组-时间复杂度O(N)、对后缀数组排序是O(N*NlogN),第一个N表示字符串的比较,后面NlogN使用快排排序。依次检测相邻两个后缀的公共长度-时间复杂度O(N*N)、取出最大公共长度的前缀-时间复杂度O(N)。

总的时间复杂度是O(N*NlogN)

编辑于 2015-02-09 17:26:03 回复(2)
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

//给定一个字符串,求出其最长的重复子串
//方法一
string lsubstr_1(const string & str)
{ 
	vector<string> vs;
	for (int i = 0; i < str.size(); i++)
		vs.push_back(str.substr(i));
	sort(vs.begin(), vs.end());
	int max = 0;
	int flag = 0;
	for (int i = 0; i <( vs.size()-1); i++)
	{
		int j = 0;
		while (vs[i][j] == vs[i + 1][j] && j<vs[i].size() && j<vs[i+1].size())
			j++;
		if (j>max)
		{
			max = j;
			flag = i;
		}			
	}
	return vs[flag].substr(0, max);
}

//方法二
string lsubstr_2(const string & str)
{
	string maxstr;
	for (int i = 0; i < str.size();i++)
	for (int j = (str.size() - i); j >=1 ; j--)
	{
		string subs = str.substr(i, j);
		int front = str.find(subs);
		int back = str.rfind(subs);
		if (front != back && subs.size() > maxstr.size())
			maxstr = subs; 
	}
	return maxstr;
}

//方法三
string lsubstr_3(const string & str)
{
	string maxstr;
	for (int i = 0; i < str.size(); i++)
		for (int j = 0; j < i; j++)
		{
			string temp;
			int k = j;
			int m = i;
			while (str[m] == str[k] && i<str.size() && k<str.size())
			{
				m++; k++;
			}
			temp = str.substr(j, k - j);
			if (temp.size()>maxstr.size())
				maxstr = temp;
		}
	return maxstr;
}

void main(void)
{
	string test;
	//cin >> test;
	getline(cin, test);
	cout << lsubstr_1(test) << endl;
	cout << lsubstr_2(test) << endl;
	cout << lsubstr_3(test) << endl;
}

发表于 2015-09-03 13:26:58 回复(2)



string FindStr(const string &str)
{
	string temp, MaxStr;
	int MaxLen = 0;
	for (int i = 0; i < str.length(); ++i)
	{
		for (int j = str.length() -i; j != 0; --j)
		{
			temp = str.substr(i, j);
			int front = str.find(temp);
			int behind = str.rfind(temp);
			int templen = temp.length();
			if (front != behind&&templen > MaxLen)
			{
				MaxStr = temp;
				MaxLen = templen;
			}
		}
	}
	return MaxStr;
}


编辑于 2017-02-07 19:16:01 回复(9)
循环去掉前面i个字符。
剩下的用KMP求next数组算法。
数组中最大值就是最长重复。
这个复杂度怎么样?
求next 复杂度 n
大循环n次
总复杂度
n平方
发表于 2017-04-02 13:29:58 回复(0)
import java.util.Scanner;

public class Main{
public static void maxStr(String string){
char[] str = string.toCharArray();
if(str==null)  return;
int max = 0;
int frist = 0;
int count = 0;
//其中i表示每次循环设定的字符串比较间隔(1,2,3。。。)。j表示遍历字符串数组。
for(int i=1;i<str.length;i++)
for(int k=0,j=0;j<str.length-i;j++){
if(str[j]==str[i+j])  k++;
else k=0;
if(k>max)  {
max = k;
frist = j-k+1;
}
}
if(max>0){
System.out.println(max);
for(;count<max;count++){
System.out.print(str[frist+count]);
}
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String str = scanner.nextLine();
maxStr(str);
}
}

}

发表于 2016-08-26 11:06:43 回复(0)
public class MaxReStr {
	public String findStr(String s){
		if(s==null){
			return null;
		}
		//最长重复子串的长度
		int max=0;
		//最长重复子串的第一个字符在s中的下标
		int first=0;
		String res = null;
		//i为每次循环设定的字符串比较间隔:1,2,...,s.length()-1
		for(int i=1;i<s.length();i++){
			for(int k=0,j=0;j<s.length()-i;j++){
				if(s.charAt(j)==s.charAt(j+i))
					k++;
				else
					k=0;
				if(k>max){
					max=k;
					first=j-max+1;
				}
			}
			if(max>0){
				res = s.substring(first, first+max);
			}
		}
		return res;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String s = "eabcdabcf";
		System.out.println(new MaxReStr().findStr(s));
	}
}
输出为  abc
发表于 2015-09-02 19:50:44 回复(2)
#include <stdlib.h>
#include <stdio.h>

#define MaxChar 5000
char a[MaxChar];
char *post[MaxChar];

int pstrcmp(const void *p1, const void *p2)
{
    return strcmp(*(char* const *)p1, *(char* const *)p2);
}

//求排序后相邻两个串的最长公共前缀
int common_len(char *p, char *q)
{
    int k = 0;
    while(*p && (*p++ == *q++))
        k++;
    return k;
}

int main()
{
    char ch;
    int i = 0, j;
    int temp;
    int max = 0, max_index = 0;
    while((ch = getchar()) != '\n')
    {
        post[i] = &a[i];//将后缀式的指针指向该后缀式的第一个字符
        a[i++] = ch;
    }
    a[i] = '\0';

    qsort(post, i, sizeof(char *), pstrcmp);//对所有后缀式进行排序

    for(j = 0; j < i - 1; j++)
    {
        temp = common_len(post[j], post[j+1]);
        if(max < temp)
        {
            max = temp;
            max_inde敏感词rintf("%d %s\n", max, post[max_index]);
    return 0;
}


发表于 2015-09-02 13:53:23 回复(0)
O(n^3)
#include <iostream>
#include <string>
using namespace std;
int main(){
    string s;
    cin >> s;
    int len = 0;
    for(int i = 0; i < s.size(); i++){
        for(int j = s.size()-i; j >= i; j--){
            string str = s.substr(i, j);
            int front = s.find(str);
            int back = s.rfind(str);
            if(front != back && j > len){
                len = j;
            }
        }
    }
    cout << len << endl;
    return 0;
}
编辑于 2015-09-01 22:18:42 回复(0)
 一个简单的实现,时间复杂度为O(n3)
string longestRepeatStr(string str) {
    int n = str.length();
    for(int i = n - 1; i > 0; --i)
         for(int j = 0; j < n; ++j) {
             if(i + j < n) {
                string cur = str.substr(j,i);
                int index1 = str.find(cur); // 从前往后找
                int index2 = str.rfind(cur); // 从后往前找
                if(index1 != index2)
                    return cur;
            }
        }
}
发表于 2015-08-29 11:53:51 回复(3)
传送门:《编程珠玑(第2版)》15.2章节“短语”。采用后缀数组。空间O(n^2),时间O(n*logn)
发表于 2015-09-05 15:00:01 回复(0)
int longest = 0;
int currentLongest = 0;
for(int i = 1; i < array.size();i++){
    if(array[i]==array[i-1]){
        currentLongest++;
    }
    else{
        currentLongest = 0;
    }
    if(currentLongest >= longest)
    {
        longest = currentLongest;
    }
}

return longest;

发表于 2018-08-30 16:24:18 回复(0)
有个疑问。
请问各位大佬,按照参考答案中"ana"和“anana”最大公共长度的不是ana吗?
那最后得出的不应该是ana?(虽然这是个明显错误的答案,但是逻辑上我有点绕不过来……不明白为什么)
发表于 2018-02-03 01:09:25 回复(0)

下面说明为什么(rand7()-1)*7+rand7()可以构造出均匀分布在1-49的随机数:
首先rand7()-1得到一个离散整数集合{0,1,2,3,4,5,6},其中每个整数的出现概率都是1/7。那么(rand7()-1)*7得到一个离散整数集合A={0,7,14,21,28,35,42},其中每个整数的出现概率也都是1/7。而rand7()得到的集合B={1,2,3,4,5,6,7}中每个整数出现的概率也是1/7。显然集合A和B中任何两个元素组合可以与1-49之间的一个整数一一对应,也就是说1-49之间的任何一个数,可以唯一确定A和B中两个元素的一种组合方式,反过来也成立。由于A和B中元素可以看成是独立事件,根据独立事件的概率公式P(AB)=P(A)P(B),得到每个组合的概率是1/7*1/7=1/49。因此(rand7()-1)*7+rand7()生成的整数均匀分布在1-49之间,每个数的概率都是1/49。


程序:

int rand7()
{
    int x=0;
    do
    {
        x=(rand7()-1)*7+rand7();
    }
    while(x>40);
    return x%10+1;
}
发表于 2017-03-29 13:16:59 回复(0)
#include <iostream>
#include <string>
using namespace std;
int main()
{
      string input;
      while(getline(cin, input))
      {
            int len = input.length();
            if(len < 1)
                 continue;
            int max_len = 1;
            int curr_len = 1;           
            for(int i = 1; i < len; i++)
            {
                 if(input[i] == input[i - 1])
                       curr_len++;
                 else 
                       curr_len = 1;
                 if(curr_len > max_len)
                       max_len = curr_len;
             }
             cout << max_len << endl;
        }
        return 0;
}
发表于 2016-08-07 11:19:37 回复(0)
http://blog.csdn.net/qunqin/article/details/7312295
发表于 2015-09-08 12:14:35 回复(0)
问个很LOW的问题,谁能讲讲排序的规则,C自学者一枚。。
发表于 2015-09-06 15:45:07 回复(0)
<div> #include&lt;stdio.h&gt; </div> <p> void LongChar(char* str)<br /> {<br /> &nbsp;if(str==NULL)<br /> &nbsp;&nbsp;return;<br /> &nbsp;int max=0;<br /> &nbsp;int first=0;<br /> &nbsp;int count=0;<br /> &nbsp;for(int i=1;i&lt;strlen(str);i++)<br /> &nbsp;&nbsp;for(int k=0,j=0;j&lt;strlen(str)-i;j++)<br /> &nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;if(str[j]==str[i+j])k++;<br /> &nbsp;&nbsp;&nbsp;else<br /> &nbsp;&nbsp;&nbsp;&nbsp;k=0;<br /> &nbsp;&nbsp;&nbsp;if(k&gt;max)<br /> &nbsp;&nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;max=k;<br /> &nbsp;&nbsp;&nbsp;&nbsp;first=j-k+1;<br /> &nbsp;&nbsp;&nbsp;} </p> <p> &nbsp;&nbsp;}<br /> &nbsp;&nbsp;if(max&gt;0)<br /> &nbsp;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;cout&lt;&lt;"long:"&lt;&lt;max&lt;&lt;endl;<br /> &nbsp;&nbsp;&nbsp;for(;count&lt;max;count++)<br /> &nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;str[first+count];<br /> &nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;endl;<br /> &nbsp;&nbsp;}<br /> } </p> <div> int main()<br /> {&nbsp; </div> <div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string a[200]; </div> <div> &nbsp;&nbsp;&nbsp;&nbsp;printf("请输入一个字符串:"); </div> <div> &nbsp;&nbsp;&nbsp;&nbsp;gets(a); </div> <div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char* str=a; </div> <div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LongChar(str);<br /> &nbsp;return 0;<br /> } </div>
发表于 2015-09-05 11:59:14 回复(0)
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;

int main()
{
	freopen("test.txt", "r", stdin);

	string str;
	while(cin>>str)
	{
		string maxStr;
		for(int i = 0; i < str.size(); i++)
		{
			for(int j = (str.size() - i); j >= 1; j--)
			{
				string subStr = str.substr(i, j);
				int start = str.find(subStr);
				int end = str.rfind(subStr);

				if(start + subStr.size() <= end && maxStr.size() < subStr.size())
					maxStr = subStr;
			}
		}
		cout<<maxStr<<endl;
	}
	return 0;
}

发表于 2015-09-04 19:41:40 回复(0)
构造一颗后缀树,在构造的过程中就能记录最长的重复序列。
构造后缀树的时间复杂度O(n^2),最终的时间复杂度为O(n^2)
发表于 2015-09-02 21:53:39 回复(0)
KMP
发表于 2015-09-01 16:57:03 回复(0)
string findString( string& s ){

	if( s.length() < 2 )
		return s;

	int index = 0;

	string res;
	int maxLen = 0;
	int len = s.length();

	for ( ; index<len-1; ++index ) {

		int j = index+1;

		while( s[index] != s[j] && j<len )
			++j;

		int count = 0;
		string tmp;

		if( j!=len ){

			int i = index;

			while( s[i] == s[j] ){

				++count;
				tmp.push_back( s[i] );
				++i; 
				++j;
			}

			if( count>maxLen ){

				maxLen = count;
				res.swap( tmp );
				tmp.clear();
			}
		}
	}

	return res;
}

发表于 2015-08-31 23:07:25 回复(0)