题目来源:

http://poj.org/problem?id=3080

 

分析:

题意要求你求出每组字符串的最长公共子串,公共子串长度要求至少为3。

注意,当出现等长的公共字符串时,输出按字典序字符串最小的那个。若没公共子串,输出“no significant commonalities。

分析: 用kmp 枚举,让第一个字符串的长度大于等于3的子串分别与其他字符串匹配比较,第一个字符串的长度 从 len 开始向下减, 直到发现一个 公共字串,则为最大 公共字串。

代码如下:

const int Max_N = 65;
char s[12][Max_N];
int next[Max_N];
//后缀数组(前缀函数)
//数组从1开始
void get_next(int m, char * p){
    char tmp[Max_N];
    strcpy(tmp+1, p);
    int i,k=0;
    next[1]=0;
    for(i=2; i<=m ; i++){
        while(k>0 && tmp[k+1] != tmp[i])
            k=next[k];
        if(tmp[k+1] == tmp[i])
            k++;
        next[i]= k;
    }
}
// 匹配算法,t1为目标串,n为目标长度, p1为模式串, m为 模式长度
// 下标都从1开始
bool kmp(char * t1, char * p1,int n, int m){
    char t[Max_N], p[Max_N];
    strcpy(t+1,t1);
    strcpy(p+1,p1);
    int i,k=0;
    int cnt=0;
    for(i=1; i<=n; i++){
        while(k>0 && p[k+1] != t[i])
            k=next[k];
        if(p[k+1] == t[i])
            k++;
        if(k == m)
            return 1;
    }
    return 0;
}
int main(){
    int t,j;
    scanf("%d",&t);
    while(t--){
        int m;
        int flag = 0;
        char res[Max_N] = "no significant commonalities";
        scanf("%d",&m);
        for(int i=0; i<m; i++)
            scanf("%s",s[i]);
        int len=strlen(s[0]);
        for(int l=len ; l>=3; l--){ // 枚举子串的长度从大到小
            for(int pos = 0; pos + l <= len ; pos++){ // 枚举第一个字符串的位置从0到最大
                get_next(l, s[0]+pos );
                for( j=1; j<m; j++){
                    int n=strlen(s[j]);
                    if( !kmp(s[j] , s[0]+ pos, n,l) )
                        break;
                }
                if(j == m ){
                    if(strcmp(res, s[0]+pos) > 0){ // 若长度相等,但从不同位置开始的,还需比较字典序最小
                        strcpy(res , s[0]+pos );
                        res[l] = '\0';   //使复制的到 l-1 处结束输出 ,这个地方 很关键
                        flag=1;
                    }
                }
            }
            if(flag)
                break;
        }
        printf("%s\n",res);
    }
    return 0;
}