给定n个字符串,求最长公共子串。
利用KMP是对子串从1..j匹配母串,枚举第一个母串的所有后缀子串,在KMP匹配过程中得到j的最大值(这里不能完全匹配后再得最大值,为了确保取到所有子串),对n-1个母串匹配完成后,j最大值的最小值便为公共子串。若有多个这样的子串,字典顺序小者为result。
这里用到了string的两个函数,strcpy(substr, str)和strncpy(substr, str, n)。前者是复制到空字符停止复制,后者则是先找到n个字符再开始复制。使用后者后,要对复制所得字符串封存,substr[n] = '\0',因为这里不知道,WA了好多次...而且好像strcpy(substr, str+i)可行,但strcpy(substr+j, str+i)不可行,让我不得不改了下get_next()和kmp()中的模板部分。
补充:刚刚做完3450那题,基本和这题一样,但是让我发现了一个对此题没有影响的BUG。若没有result.len>=3 这个条件,输入abc cde是得不到结果c的,原因在于我的KMP结束后返回的max是substr的下标,也就是说max=3则说明取substr[0..3]四个字符,当只有一个字符时max=0,若m初始化为0,则没有字符符合时返回也为0。
就这点小BUG让我调3450调了3个小时。。共WA14次。。
code:
#include<cstdio>
#include<cstring>
char str[10][62] ;
char substr[62] ;
char result[62] ;
char temp[62] ;
int next[62] ;
int len, n, max ;
void get_next(){
next[0] = -1 ;
int j = -1 ;
for(int i=1; i<len; i++){
while(j>-1&&substr[j+1]!=substr[i])
j = next[j] ;
if(substr[j+1]==substr[i]) j ++ ;
next[i] = j ;
}
}
void kmp(){
int j, m ;
get_next() ;
for(int k=1; k<n; k++){
j = -1, m = -1 ;//m最好取-1
for(int i=0; i<60; i++){
while(j>-1&&substr[j+1]!=str[k][i])
j = next[j] ;
if(substr[j+1]==str[k][i]) j ++ ;
if(j>m) m = j ; //m取可匹配到的最大值
}
if(m<max) max = m ;//max取可匹配到的最小值,公共子串以最小者为准
}
}
int main(){
int t, i, ans ;
scanf("%d", &t) ;
while(t--){
scanf("%d", &n) ;
for(i=0; i<n; i++)
scanf("%s", str[i]) ;
ans = 0 ;
for(i=0; i<58; i++){
strcpy(substr, str[0]+i) ;//枚举第一个串的所有后缀串
len = 60 - i ;
max = 65 ;
kmp() ;
if(max>ans){
ans = max ;
strncpy(result, str[0]+i, ans+1) ;
result[ans+1] = '\0' ;
}else if(max==ans){ //若有相等长度,取字典序小者
strncpy(temp, str[0]+i, ans+1) ;
temp[ans+1] = '\0' ;
if(strcmp(result, temp)>0)
strcpy(result, temp) ;
}
}
if(ans>=2)
printf("%s\n", result) ;
else
printf("no significant commonalities\n") ;
}
return 0 ;
#include<cstring>
char str[10][62] ;
char substr[62] ;
char result[62] ;
char temp[62] ;
int next[62] ;
int len, n, max ;
void get_next(){
next[0] = -1 ;
int j = -1 ;
for(int i=1; i<len; i++){
while(j>-1&&substr[j+1]!=substr[i])
j = next[j] ;
if(substr[j+1]==substr[i]) j ++ ;
next[i] = j ;
}
}
void kmp(){
int j, m ;
get_next() ;
for(int k=1; k<n; k++){
j = -1, m = -1 ;//m最好取-1
for(int i=0; i<60; i++){
while(j>-1&&substr[j+1]!=str[k][i])
j = next[j] ;
if(substr[j+1]==str[k][i]) j ++ ;
if(j>m) m = j ; //m取可匹配到的最大值
}
if(m<max) max = m ;//max取可匹配到的最小值,公共子串以最小者为准
}
}
int main(){
int t, i, ans ;
scanf("%d", &t) ;
while(t--){
scanf("%d", &n) ;
for(i=0; i<n; i++)
scanf("%s", str[i]) ;
ans = 0 ;
for(i=0; i<58; i++){
strcpy(substr, str[0]+i) ;//枚举第一个串的所有后缀串
len = 60 - i ;
max = 65 ;
kmp() ;
if(max>ans){
ans = max ;
strncpy(result, str[0]+i, ans+1) ;
result[ans+1] = '\0' ;
}else if(max==ans){ //若有相等长度,取字典序小者
strncpy(temp, str[0]+i, ans+1) ;
temp[ans+1] = '\0' ;
if(strcmp(result, temp)>0)
strcpy(result, temp) ;
}
}
if(ans>=2)
printf("%s\n", result) ;
else
printf("no significant commonalities\n") ;
}
return 0 ;
}