問題描述
給定一個字符串,求出其最長重復子串
例如:abcdabcd
最長重復子串是 abcd,最長重復子串可以重疊
例如:abcdabcda,這時最長重復子串是 abcda,中間的 a 是被重疊的。
直觀的解法是,首先檢測長度為 n - 1 的字符串情況,如果不存在重復則檢測 n - 2, 一直遞減下去,直到 1 。
這種方法的時間復雜度是 O(N * N * N),其中包括三部分,長度緯度、根據長度檢測的字符串數目、字符串檢測。
改進的方法是利用后綴數組
后綴數組是一種數據結構,對一個字符串生成相應的后綴數組后,然后再排序,排完序依次檢測相鄰的兩個字符串的開頭公共部分。
這樣的時間復雜度為:生成后綴數組 O(N),排序 O(NlogN*N) 最后面的 N 是因為字符串比較也是 O(N)
依次檢測相鄰的兩個字符串 O(N * N),總的時間復雜度是 O(N^2*logN),優于第一種方法的 O(N^3)
???? 對于類似從給定的文本中,查找其中最長的重復子字符串的問題,可以采用“后綴數組”來高效地完成此任務。后綴數組使用文本本身和n個附加指針(與文本數組相應的指針數組)來表示輸入文本中的n個字符的每個子字符串。
?? 首先,如果輸入字符串存儲在c[0..n-1]中,那么就可以使用類似于下面的代碼比較每對子字符串:
~~~
int main(void)
{
int i , j , thislen , maxlen = -1;
......
......
......
for(i = 0 ; i < n ; ++i )
{
for(j = i+1 ; j < n ; ++j )
{
if((thislen = comlen(&c[i] , &c[j])) > maxlen)
{
maxlen = thislen;
maxi = i;
maxj = j;
}
}
}
......
......
......
return 0;
}
~~~
當作為comlen函數參數的兩個字符串長度相等時,該函數便返回這個長度值,從第一個字符開始:
~~~
int comlen( char *p, char *q )
{
int i = 0;
while( *p && (*p++ == *q++) )
++i;
return i;
}
~~~
?? 由于該算法查看所有的字符串對,所以它的時間和n的平方成正比。下面便是使用“后綴數組”的解決辦法。
?? 如果程序至多可以處理MAXN個字符,這些字符被存儲在數組c中:
~~~
#define MAXCHAR 5000 //最長處理5000個字符
char c[MAXCHAR], *a[MAXCHAR];
~~~
在讀取輸入時,首先初始化a,這樣,每個元素就都指向輸入字符串中的相應字符:
~~~
n = 0;
while( (ch=getchar())!='\n' )
{
a[n] = &c[n];
c[n++] = ch;
}
c[n]='\0'; // 將數組c中的最后一個元素設為空字符,以終止所有字符串
~~~
這樣,元素a[0]指向整個字符串,下一個元素指向以第二個字符開始的數組的后綴,等等。如若輸入字符串為"banana",該數組將表示這些后綴:
a[0]:banana
a[1]:anana
a[2]:nana
a[3]:ana
a[4]:na
a[5]:a
由于數組a中的指針分別指向字符串中的每個后綴,所以將數組a命名為"后綴數組"
第二、對后綴數組進行快速排序,以將后綴相近的(變位詞)子串集中在一起
qsort(a, n, sizeof(char*), pstrcmp)后
a[0]:a
a[1]:ana
a[2]:anana
a[3]:banana
a[4]:na
a[5]:nana
第三、使用以下comlen函數對數組進行掃描比較鄰接元素,以找出最長重復的字符串:
~~~
for(i = 0 ; i < n-1 ; ++i )
{
temp=comlen( a[i], a[i+1] );
if( temp>maxlen )
{
maxlen=temp;
maxi=i;
}
}
printf("%.*s\n",maxlen, a[maxi]);
~~~
完整的實現代碼如下:
~~~
#include <iostream>
using namespace std;
#define MAXCHAR 5000 //最長處理5000個字符
char c[MAXCHAR], *a[MAXCHAR];
int comlen( char *p, char *q )
{
int i = 0;
while( *p && (*p++ == *q++) )
++i;
return i;
}
int pstrcmp( const void *p1, const void *p2 )
{
return strcmp( *(char* const *)p1, *(char* const*)p2 );
}
int main(void)
{
char ch;
int n=0;
int i, temp;
int maxlen=0, maxi=0;
printf("Please input your string:\n");
n = 0;
while( (ch=getchar())!='\n' )
{
a[n] = &c[n];
c[n++] = ch;
}
c[n]='\0'; // 將數組c中的最后一個元素設為空字符,以終止所有字符串
qsort( a, n, sizeof(char*), pstrcmp );
for(i = 0 ; i < n-1 ; ++i )
{
temp=comlen( a[i], a[i+1] );
if( temp>maxlen )
{
maxlen=temp;
maxi=i;
}
}
printf("%.*s\n",maxlen, a[maxi]);
return 0;
}
~~~
方法二:KMP
通過使用next數組的特性,同樣可以求最長重復子串,不過時間復雜度有點高挖。。
~~~
#include<iostream>
using namespace std;
const int MAX = 100000;
int next[MAX];
char str[MAX];
void GetNext(char *t)
{
int len = strlen(t);
next[0] = -1;
int i = 0 , j = -1;
while(i < len)
{
if(j == -1 || t[i] == t[j])
{
i++;
j++;
if(t[i] != t[j])
next[i] = j;
else
next[i] = next[j];
}
else
j = next[j];
}
}
int main(void)
{
int i , j , index , len;
cout<<"Please input your string:"<<endl;
cin>>str;
char *s = str;
len = 0;
for(i = 0 ; *s != '\0' ; s++ , ++i)
{
GetNext(s);
for(j = 1 ; j <= strlen(s) ; ++j)
{
if(next[j] > len)
{
len = next[j];
index = i + j; //index是第一個最長重復串在str中的位置
}
}
}
if(len > 0)
{
for(i = index - len ; i < index ; ++i)
cout<<str[i];
cout<<endl;
}
else
cout<<"none"<<endl;
return 0;
}
~~~
題目描述:求最長不重復子串,如abcdefgegcsgcasse,最長不重復子串為abcdefg,長度為7
~~~
#include <iostream>
#include <list>
using namespace std;
//思路:用一個數組保存字符出現的次數。用i和j進行遍歷整個字符串。
//當某個字符沒有出現過,次數+1;出現字符已經出現過,次數+1,找到這個字符前面出現的位置的下一個位置,設為i
//并將之前的那些字符次數都-1。繼續遍歷,直到'\0'
int find(char str[],char *output)
{
int i = 0 , j = 0;
int cnt[26] = {0};
int res = 0 , temp = 0;
char *out = output;
int final;
while(str[j] != '\0')
{
if(cnt[str[j]-'a'] == 0)
{
cnt[str[j]-'a']++;
}
else
{
cnt[str[j]-'a']++;
while(str[i] != str[j])
{
cnt[str[i]-'a']--;
i++;
}
cnt[str[i]-'a']--;
i++;
}
j++;
temp = j-i;
if(temp > res)
{
res = temp;
final = i;
}
}
//結果保存在output里面
for(i = 0 ; i < res ; ++i)
*out++ = str[final++];
*out = '\0';
return res;
}
int main(void)
{
char a[] = "abcdefg";
char b[100];
int max = find(a,b);
cout<<b<<endl;
cout<<max<<endl;
return 0;
}
~~~
- 前言
- 程序員有趣的面試智力題
- 淘寶網 校園招聘 技術人員筆試題
- 網新恒天2011.9.21招聘會筆試題
- 淘寶2011.9.21校園招聘會筆試題
- 騰訊2011.10.15校園招聘會筆試題
- 網易游戲2011.10.15校園招聘會筆試題
- 百度2011.10.16校園招聘會筆試題
- 微策略2011校園招聘筆試題(找出數組中兩個只出現一次的數字)
- 百度最新面試題集錦
- C/C++筆試題目大全
- 各大IT公司校園招聘程序猿筆試、面試題集錦
- Trie樹詳解及其應用
- 后綴數組求最長重復子串
- 海量數據隨機抽樣問題(蓄水池問題)
- 搜狐2012.9.15校園招聘會筆試題
- 搜狗2012.9.23校園招聘會筆試題
- Google2012.9.24校園招聘會筆試題
- 優酷土豆2012.9.12校園招聘會筆試題