zoukankan      html  css  js  c++  java
  • POJ 1226 Substrings (后缀数组)

    Substrings
    Time Limit: 1000MS   Memory Limit: 10000K
    Total Submissions: 10244   Accepted: 3515

    Description

    You are given a number of case-sensitive strings of alphabetic characters, find the largest string X, such that either X, or its inverse can be found as a substring of any of the given strings.

    Input

    The first line of the input contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. The first line of each test case contains a single integer n (1 <= n <= 100), the number of given strings, followed by n lines, each representing one string of minimum length 1 and maximum length 100. There is no extra white space before and after a string.

    Output

    There should be one line per test case containing the length of the largest string found.

    Sample Input

    2
    3
    ABCD
    BCDFF
    BRCD
    2
    rose
    orchid

    Sample Output

    2
    2 

    Source

     
     
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    
    const int maxn=20200;
    
    int wa[maxn],wb[maxn],wv[maxn], ws[maxn];
    
    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 *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb,*t;
        for(i=0;i<m;i++)    ws[i]=0;
        for(i=0;i<n;i++)    ws[x[i]=r[i]]++;
        for(i=1;i<m;i++)    ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--)     sa[--ws[x[i]]]=i;
        for(j=1,p=1;p<n;j<<=1,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++)    ws[i]=0;
            for(i=0;i<n;i++)    ws[wv[i]]++;
            for(i=1;i<m;i++)    ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--)     sa[--ws[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++;
        }
    }
    
    int rank[maxn],height[maxn];
    
    void calheight(int *r,int *sa,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 r[maxn],sa[maxn];
    int who[maxn],yes[110]={0},cnt=0;
    int len,n;
    
    int check(int mid){
        int i,j,k,t,s;
        for(i=2;i<=len;i=j+1){
            for(;height[i]<mid && i<=len;i++) ;
            for(j=i;height[j]>=mid;j++) ;
            if(j-i+1<n)
                continue;
            cnt++;
            s=0;
            for(k=i-1;k<j;k++)
                if((t=who[sa[k]])!=0 && yes[t]!=cnt)
                    yes[t]=cnt,s++;
            if(s>=n)
                return 1;
        }
        return 0;
    }
    
    char st[110];
    int main()
    {
        //freopen("input.txt","r",stdin);
    
        int i,j,k,min,mid,max,nn;
        scanf("%d",&nn);
        while(nn-->0)
        {
          scanf("%d",&n);
          len=0;
          for(i=1;i<=n;i++)
          {
            scanf("%s",st);
            k=strlen(st);
            for(j=0;j<k;j++)
            {
              r[j+len]=st[j]+200;
              who[j+len]=i;
            }
            r[len+k]=2*i-1;
            who[len+k]=0;
            len+=k+1;
            for(j=0;j<k;j++)
            {
              r[j+len]=st[k-1-j]+200;
              who[j+len]=i;
            }
            r[len+k]=2*i;
            who[len+k]=0;
            len+=k+1;
          }
          len--;
          r[len]=0;
          da(r,sa,len+1,328);
          calheight(r,sa,len);
          height[len+1]=-1;
          min=1;max=100;
          while(min<=max)
          {
            mid=(min+max)>>1;
            if(check(mid)) min=mid+1;
            else max=mid-1;
          }
          if(n==1) max=len/2;
          printf("%d\n",max);
        }
        return 0;
    }

    2,标准库函数strncpy(),可以将一字符串的一部分拷贝到另一个字符串中。strncpy()函数有3个参数:第一个参数是目录字符串;第二个参数是源字符串;第三个参数是一个整数,代表要从源字符串拷贝到目标字符串中的字符数

    题意:给定n个串,求一个最大子串长度,使得它或者它的逆向串在每个串中出现。

    思路:先求出最短的串sho[],然后枚举答案长度ans(len~1)。于是sho[]就被分成了len-ans+1个子串pos[],再分别求出这些字串的反串inv[],判断pos[]和inv[]是否都出现在所有的串中。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    
    int n;
    char str[110][110],sho[110];
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            int len=110,k;
            for(int i=0;i<n;i++){
                scanf("%s",str[i]);
                int tmp=strlen(str[i]);
                if(tmp<len){
                    len=tmp;
                    k=i;    // 找出最短的串。
                }
            }
            strcpy(sho,str[k]);
            int ans=len,flag=0;
            char pos[110],inv[110];
            while(ans>0){
                for(int i=0;i<=len-ans;i++){
                    strncpy(pos,sho+i,ans); // 求出正序子串pos[]。 
                    for(int j=0;j<ans;j++)
                        inv[j]=pos[ans-1-j];    // 求出反序子串inv[]。
                    pos[ans]=inv[ans]='\0';
                    flag=1;
                    for(int j=0;j<n;j++)
                        if(!strstr(str[j],pos) && !strstr(str[j],inv)){ // 判断pos[],inv[]是否都出现在n个串中。运用strstr函数。
                            flag=0;
                            break;
                        }
                    if(flag)
                        break;
                }
                if(flag)
                    break;
                else
                    ans--;
            }
            printf("%d\n",ans);
        }
        return 0;
    }

    3,KMP

    /*
    * 1226 KMP
    *
    * 用的KMP, O(n^3)
    * 由于数据规模小, 直接暴力枚举最短串的各个子串再依次与其他串KMP应该也能过 (复杂度也是O(n^3))
    *
    * 这里不直接枚举各个子串, 而是枚举子串的起点位置, 依次与其他串KMP, 记录和每个串及其反串的最大匹配长度,
    * 再以此计算这些值的最小值, 就得到该起点的子串所能达到的最大匹配长度(与刚才的方法其实差不多)
    *
    */
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int maxN = 100 + 5;
    const int maxL = 100 + 5;
    //minLen, minNum : 最短串
    int caseNum, n, minLen, minNum;
    int next[maxL];
    //串 反串
    char str[maxN][maxL], restr[maxN][maxL];
    
    
    void reverseString(int i, int len){
        for(int j=0; j<len; j++)
            restr[i][j] = str[i][len - j -1];
        restr[i][len] = 0;
    }
    
    //以start为起点的模式
    void preKMP(int start){
        int j, k;
        next[start] = start - 1;
        j = start, k = start - 1;
        while(j < minLen - 1){
            while(k > start - 1 && str[minNum][j] != str[minNum][k]){
                k = next[k];
            }
            j++, k++;
            if(str[minNum][j] == str[minNum][k])
                next[j] = next[k];
            else
                next[j] = k;
        }
    }
    
    
    int KMP(int t, int start){
        int i, j, ansMax, tmpMax = start;
        int tLen = strlen(str[t]);
    
        i = 0; j = start;
        while(i < tLen && j < minLen){
    
            while(j >= start && str[t][i] != str[minNum][j]){
                j = next[j];
            }
            i++, j++;
            //记录最大的j
            if(tmpMax < j)
                tmpMax = j;
        }
        ansMax = tmpMax - start;
        tmpMax = start;
        i = 0; j = start;
        while(i < tLen && j <minLen){
    
            while(j >= start && restr[t][i] != str[minNum][j]){
                j = next[j];
            }
            i++, j++;
            if(tmpMax < j)
                tmpMax = j;
        }
        //串与反串的最大j
        if(ansMax < tmpMax - start)
            ansMax = tmpMax - start;
    
        return ansMax;
    }
    
    
    int main(){
        scanf("%d", &caseNum);
        while(caseNum--){
            scanf("%d", &n);
    
            //注意n == 1 , 为此WA了两次!!
            if(n == 1){
                scanf("%s", str[0]);
                printf("%d\n", strlen(str[0]));
                continue;
            }
    
            minLen = maxL;
            for(int i=0; i<n; i++){
                scanf("%s", str[i]);
                int len = strlen(str[i]);
                reverseString(i, len);
    
                if(len < minLen){
                    minLen = len;
                    minNum = i;
                }
            }
    
            int tmpMin = maxL, tmp, tmpMax = -1;
            //枚举起点
            for(int i=0; i<minLen; i++){
                preKMP(i);
                tmpMin = maxL;
                for(int j=0; j<n; j++){
                    if(j == minNum) continue;
    
                    tmp = KMP(j, i);
                    if(tmp < tmpMin)
                        tmpMin = tmp;
                }
                
                if(tmpMax < tmpMin)
                    tmpMax = tmpMin;
            }
            printf("%d\n", tmpMax);
    
        }
    
        return 0;
    }
  • 相关阅读:
    对mysql 数据库操作 使其支持插入中文(针对python)
    网页制作中的超链接怎么做
    python requests的安装与简单运用(转)
    ubuntu 修改权限
    [Linux]常用命令与目录全拼
    Python web 简单服务器的搭建与运行
    ubuntu 下终端关于调试C++的命令
    正则表达式基本语法详解
    Linux下 编译C++/C以及常用的几种命令(ubuntu)
    Visual Studio 2013 中 mysql 使用 EF6
  • 原文地址:https://www.cnblogs.com/jackge/p/3094486.html
Copyright © 2011-2022 走看看