Posted on 2012-12-05 17:58
hoshelly 閱讀(1141)
評論(0) 編輯 收藏 引用 所屬分類:
Programming
描述
對于一個字符串S1,其中S2是他的一個子串(長度嚴格小于S1長度),如果S2在S1中出現(xiàn)次數(shù)超過1次,那么S2就是一個重復(fù)子串,現(xiàn)在的要求是給定S1,請求出他的最長重復(fù)子串;
如果有多個長度一樣的最長子串,請輸入字典序最小那個串;
比如bbbaaaccc
那么最長子串就是aa
輸入
第一行包含一個整數(shù)T,表示有T組數(shù)據(jù)
對于每組數(shù)據(jù)包含一行,該行有一個字符串,長度小于10,000
輸出
對于每組數(shù)據(jù)請輸出他的最長重復(fù)子串,保證每組數(shù)據(jù)都有;
樣例輸入
2
abacabac
abacabbac
樣例輸出
abac
bac
代碼測試通過(普通版):
#include<stdio.h>
#include<string.h>
#define N 10000
int main()
{
char a[N];
int i,j,n,t,p,max,t1;
scanf("%d",&t1);
while(t1--)
{
max = 0;
scanf("%s",a);
n=strlen(a);
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
t=0;
while(a[i+t]==a[j+t]&&(j+t)<n)
t++;
if(t>max)
{
max=t;
p=i;
}
else if(t == max) //如果有長度一樣的最長重復(fù)子串,那么比較它們的字典序
{
if(a[i]<a[p])
{
max = t;
p = i;
}
}
}
}
for(i=p;i<p+max;i++)
printf("%c",a[i]);
printf("\n");
}
return 0;
}
普通算法效率較低,為O(n²)。
第二種方法是用后綴數(shù)組實現(xiàn)。轉(zhuǎn)自:http://hi.baidu.com/qwertlooker/item/44f3fe52ad772cdbd58bacfd
如果程序至多可以處理MAXN個字符,這些字符被存儲在數(shù)組c中:
#define MAXN 5000000
char c[MAXN], *a[MAXN];
在讀取輸入時,首先初始化a,這樣,每個元素就都指向輸入字符串中的相應(yīng)字符:
while (ch = getchar()) != EOF
a[n] = &c[n];
c[n++] = ch;
c[n] = 0 //將數(shù)組c中的最后一個元素設(shè)為空字符,以終止所有字符串
這樣,元素a[0]指向整個字符串,下一個元素指向以第二個字符開始的數(shù)組的后綴,等等。如若輸入字符串為"banana",該數(shù)組將表示這些后綴:
a[0]:banana
a[1]:anana
a[2]:nana
a[3]:ana
a[4]:na
a[5]:a
由于數(shù)組a中的指針分別指向字符串中的每個后綴,所以將數(shù)組a命名為"后綴數(shù)組"
第二,對后綴數(shù)組進行快速排序,以將后綴相近的(變位詞)子串集中在一起
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函數(shù)對數(shù)組進行掃描比較鄰接元素,以找出最長重復(fù)的字符串:
for i = [0, n)
if comlen(a[i], a[i+1]) > maxlen
maxlen = comlen(a[i], a[i+1])
maxi = i
printf("%.*s\n", maxlen, a[maxi])
由于少了內(nèi)層循環(huán),只是多了一次排序,因此該算法的運行時間為O(n logn). (nlogn比n大,取nlogn)
實現(xiàn)代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCHAR 10000 //最長處理10000個字符
char c[MAXCHAR], *a[MAXCHAR];
int comlen( char *p, char *q ){ //計算最長重復(fù)子串的長度
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( ){
int t;
char ch;
int i, temp;
scanf("%d\n",&t);
while(t--)
{
int n=0;
int maxlen=0, maxi=0;
while( (ch=getchar())!='\n' ){
a[n]=&c[n];
c[n++]=ch;
}
c[n]='\0';
qsort( a, n, sizeof(char*), pstrcmp ); //快速排序?qū)缶Y數(shù)組進行排序,以使后綴相同的子串集中在一起,
//以便接下來comlen函數(shù)對這些子串進行計算其最長重復(fù)子串
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]); //輸出最長重復(fù)子串
}
return 0;
}
第三種方法似乎可以用后綴樹實現(xiàn),效率可以提高到O(n),具體的后綴樹講解可以參照這篇文章:
http://blog.csdn.net/v_july_v/article/details/6897097(PS:智商有限,后面部分講解理解不了)