poj 1226 求多串最长公共子串 或 回文子串 KMP&&strstr&&后缀数组

来源:互联网 发布:淘宝怎么没有朋友代付 编辑:程序博客网 时间:2024/06/06 03:49

题意:求给出的字符串中的最长公共子串 ,其中子串可以反转,比如rose 和 orchid , ro 和第一个匹配 , ro 的回文串or 和orchid 匹配 。最后输出最长子串的长度。

思路:找出长度最小的子串, 枚举其所有子串 ,然后将其反转再保存 ,最后用这两个串去匹配其它的字符串。

#include<iostream>#include<cstdio>#include<cstring>using namespace std;char str[52][505];char ss[305] , ts1[305] , ts2[305];int next[205];void get_next(char *T , int len) {      /*****得到next数组*****/     int j = 0, k = -1;    next[0] = -1;    while (j < len) {        if(T[j] == T[k] || k == -1) {            next[j+1] = k+1;            j++;k++;            }        else k = next[k];        }    }int kmp(char *T , int h , int len2 , int len1) { /*****KMP*****/    int i , j;    i = j = 0;     while(i <len1 && j <len2) {        if(T[j]==str[h][i] || j == -1) {            j++;i++;            }else {        j = next[j];        }    }     if(j == len2) return 1;    return 0;   }int main() {    int T , i , j , k , len , n , g , temp;    scanf("%d",&T);    while(T--) {        scanf("%d",&n);        for(i = 0 ; i < n ; i ++)        scanf("%s",str[i]);        len = 120;        for(i = 0 ; i < n ; i ++) {         /*****找长度最小的字符串*****/             if(strlen(str[i]) <= len) {                len = strlen(str[i]);                temp = i;                }            }        int tt , ans , cnt , l1 , l2 , lenn , f , flag , r , t;        ans = 0;        for(i = 1 ; i <= len ; i ++) {            for(j = 0 ; j <= len - i; j ++) {                for(g = 0 ,k = j ; k < j + i ; k ++ , g++) { /*****暴力枚举长度是i的最小字符串子串ts1*****/                     ts1[g] = str[temp][k];                            }                ts1[g] = '\0';               for(tt = 0 ; tt < g ; tt ++) {                 /*****求这个子串的反转子串ts2*****/                     ts2[tt] = ts1[g - tt - 1];                    }                ts2[tt] = '\0';                get_next(ts1 , i);                             /*****得到ts1的next数组*****/                 cnt = 0;                bool vis[100];                             /*****标记数组*****/                 memset(vis , false , sizeof(vis));                for(f = 0 ; f < n; f ++) {                    lenn = strlen(str[f]);                    if(kmp(ts1 , f , i , lenn) && !vis[f]) {                        vis[f] = 1; cnt++;}                 }                get_next(ts2 , i);                for(f = 0 ; f < n ; f ++) {                    lenn = strlen(str[f]);                    if(kmp(ts2 , f , i , lenn)&&!vis[f]) {                        vis[f] = 1; cnt++;}                 }                                              /*****分别用ts1 、 ts1 去匹配其它串*****/                 if(cnt == n) {                                 //满足条件中取最长的子串长度                     if(i > ans) ans = i;                    }            }            }          printf("%d\n",ans);     }    }

strstr 函数水的~   注意strncpy枚举子串的方法 

#include<iostream>#include<cstdio>#include<cstring>using namespace std;char str[120][120];char s1[120] , s2[120];char zz1[120] , zz2[120];int main() {    int n , T , i , j , k , t , temp;    scanf("%d",&T);    while(T--) {        scanf("%d",&n);        int len = 120;        for(i = 0 ; i < n ; i ++) {            scanf("%s",str[i]);            int ll = strlen(str[i]);            if(ll < len) {                len = ll;                temp  = i;            }          }        strcpy(s1 , str[temp]);        int ans = 0;        for(i = 1 ; i <= len ; i ++) { //枚举子串长度            for(j = 0 ; j <= len - i ; j ++) {                strncpy(zz1, s1+j , i);                zz1[i] = '\0';      //子串                 //printf("%s\n",zz1);                for(k = 0 ; k < i ; k ++)                zz2[k] = zz1[i - k - 1];                zz2[k] = '\0';     //反转子串                 //printf("%s\n",zz2);                for(t = 0 ; t < n ; t ++) {                    if(strstr(str[t] , zz1)||strstr(str[t] , zz2)) continue;                    else break;                    }                if(t == n) ans = i>ans?i:ans;             }           }        printf("%d\n",ans);         }    }


后缀数组的方法:和求多串公共子串差不多的方法,不过这题要连接的不只给出的字符串,也要把给出的子串的反转串连接起来,并且loc数组的状态要和正串是一样的。

最后二分枚举最短字符串的子串长度,判断即可。

#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int maxn = 21000;char str[maxn];int num[maxn] , loc[maxn];int sa[maxn] , rank[maxn] , height[maxn];int wa[maxn] , wb[maxn] , wv[maxn] , wd[maxn];int n;int cmp(int *r , int a , int b , int l) {    return r[a] == r[b] && r[a+l] == r[b+l];    }void da(int *r , int n , int m) {    int i , j , p;    int *x = wa , *y = wb , *t;    for(i = 0 ; i < m ; i ++) wd[i] = 0;    for(i = 0 ; i < n ; i ++) wd[x[i]=r[i]]++;    for(i = 1 ; i < m ; i ++) wd[i] += wd[i-1];    for(i = n-1 ; i >= 0 ; i --) sa[--wd[x[i]]] = i;    for(j = 1 , p = 1 ; p < n ; j *= 2 , m = p) {        for(p = 0 , i = n - j ; i < n ; i ++) y[p++] = i;        for(i = 0 ; i < n ; i ++) if(sa[i] >= j) y[p++] = sa[i] - j;        for(i = 0 ; i < n ; i ++) wv[i] = x[y[i]];        for(i = 0 ; i < m ; i ++) wd[i] = 0;        for(i = 0 ; i < n ; i ++) wd[wv[i]] ++;        for(i = 1 ; i < m ; i ++) wd[i] += wd[i-1];        for(i = n - 1 ; i >= 0 ; i --) sa[--wd[wv[i]]] = y[i];        for(t = x , x = y , y = t , p = 1 , x[sa[0]] = 0 , i = 1 ; i < n ; i ++) {            x[sa[i]] = cmp(y , sa[i-1] , sa[i] , j) ? p - 1: p ++;                }       }    }void calheight(int *r , int n) {    int i , j , k = 0;    for(i = 1 ; i <= n ; i ++) rank[sa[i]] = i;    for(i = 0 ; i < n ; height[rank[i++]] = k) {        for(k ? k --: 0 , j = sa[rank[i]-1] ; r[i+k]==r[j+k] ; k ++);        } }int vis[1300];bool check(int mid , int len) {    int i , j , cnt;    cnt = 0;    memset(vis , 0 , sizeof(vis));    for(i = 2 ; i <= len ; i ++) {        //printf("%d\n",height[i]);        if(height[i] < mid) {            memset(vis,0,sizeof(vis));            cnt = 0;            continue;            }            if(!vis[loc[sa[i-1]]]) {            vis[loc[sa[i-1]]] = 1;            cnt ++;                }        if(!vis[loc[sa[i]]]) {            vis[loc[sa[i]]] = 1;            cnt ++;            }        if(cnt == n) return 1;    }    return 0;    }int main() {    int T , i , j , g , k , temp;    scanf("%d",&T);    while(T--) {        scanf("%d",&n);        g = 0;        temp = 130;        int ll = 120;        for(i = 0 ; i < n ; i ++) {            scanf("%s",str);            int len = strlen(str);            if(len < ll) ll = len;            for(j = 0 ; j < len ; j ++) {                loc[g] = i;                    num[g++] = str[j];            }            loc[g] = temp;            num[g++] = temp++;            for(j = 0 ; j < len ; j ++) {                loc[g] = i;                    num[g++] = str[len - j - 1];            }            loc[g] = temp;            num[g++] = temp ++;            }           num[g] = 0;        da(num , g+1 , temp);        calheight(num , g);        int left , right , mid , ans;        left = 0;right = ll;        while(left <= right) {            mid = (left+right)/2;            if(check(mid , g)) {                left = mid + 1;                ans = mid;                    } else {                right = mid - 1;                //ans = mid;                }        }        printf("%d\n",ans);    }       }


原创粉丝点击