2016MUTC9-1010 Jong Hyok and String

題解:將p串翻轉,間隔不同字符連接起來,進行一次後綴數組的操作。若strange set(Q)>0,那麼Q肯定是p串中一個的子串。所以Q串翻轉後,二分直接在後綴數組中找到邊界位置,lower和upper。

當不存在時,答案爲0;

當lower==upper時,答案爲最大長度(sa[lower]到間隔字符的長度)-最小長度(max(height[lower],height[upper+1])+1)+1

當lower<upper時,

與Q串數量相同的最大長度是min(height[lower+1],……,height[upper]),若長度再加1,那麼子集數將會減少;

與Q串數量相同的最小長度是max(height[lower],height[upper+1])+1

兩者相減就是答案。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

const int N=2e5+10;

char str[N];
int s[N],pos[N];
int sa[N],t[N],t2[N],c[N],n;
void build_sa(int m){
    int i,*x=t,*y=t2;
    for (i=0;i<m;i++)c[i]=0;
    for (i=0;i<n;i++)c[x[i]=s[i]]++;
    for (i=1;i<m;i++)c[i]+=c[i-1];
    for (i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for (int k=1;k<=n;k<<=1){
        int p=0;
        for (i=n-k;i<n;i++)y[p++]=i;
        for (i=0;i<n;i++)if (sa[i]>=k)y[p++]=sa[i]-k;
        for (i=0;i<m;i++)c[i]=0;
        for (i=0;i<n;i++)c[x[y[i]]]++;
        for (i=0;i<m;i++)c[i]+=c[i-1];
        for (i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for (i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
        if (p>=n)break;
        m=p;
    }
}
int Rank[N],height[N];
int dp[N][20];
void getHeight(){
    int i,j,k=0;
    for (i=0;i<n;i++)Rank[sa[i]]=i;
    for (i=0;i<n;i++){
        if (k)k--;
        int j=sa[Rank[i]-1];
        while (s[i+k]==s[j+k])k++;
        dp[Rank[i]][0]=height[Rank[i]]=k;
    }
    for (int k=1;(1<<k)<=n;k++)for (int i=0;i+(1<<k)-1<n;i++)
        dp[i][k]=min(dp[i][k-1],dp[i+(1<<(k-1))][k-1]);
}
int cmp(int x,int Len)
{
    for (int i=0;i<Len;i++)
        if (s[x+i]<t[i])return -1;else if (s[x+i]>t[i])return 1;
    return 0;
}
int getmin(int L,int R)
{
    int k=0;while ((1<<(k+1))<=R-L+1)k++;
    return min(dp[L][k],dp[R-(1<<k)+1][k]);
}
void work()
{
    int m;
    scanf("%d%d",&n,&m);
    int nn=0;
    for (int bp=1;bp<=n;bp++){
        scanf("%s",str);int Len=strlen(str);
        reverse(str,str+Len);
        for (int i=0;i<Len;i++)s[i+nn]=str[i]-'a'+1,pos[i+nn]=Len+nn;
        s[Len+nn]=26+bp;pos[Len+nn]=Len+nn;nn+=Len+1;
    }
    s[nn]=0;nn++;
    swap(nn,n);build_sa(nn+27);//需要有一個比'a'小的字符,然後m不要開小
    getHeight();
    while (m--){
        scanf("%s",str);int Len=strlen(str);
        reverse(str,str+Len);
        for (int i=0;i<Len;i++)t[i]=str[i]-'a'+1;t[Len]=0;
        int lower=-1,upper;
        int L=0,R=n-1;
        while (L<=R){
            int mid=(L+R)>>1;
            int tmp=cmp(sa[mid],Len);
            if (tmp==0){
                lower=mid;R=mid-1;
            }else if (tmp>0)R=mid-1;
            else L=mid+1;
        }
        if (lower==-1){puts("0");continue;}
        L=0,R=n-1;
        while (L<=R){
            int mid=(L+R)>>1;
            int tmp=cmp(sa[mid],Len);
            if (tmp==0){
                upper=mid;L=mid+1;
            }else if (tmp>0)R=mid-1;
            else L=mid+1;
        }
        if (lower==upper)printf("%d\n",pos[sa[lower]]-sa[lower]-max(height[lower],height[upper+1]));else{
            printf("%d\n",getmin(lower+1,upper)-max(height[lower],height[upper+1]));
        }
    }
}
int main()
{
    freopen("data.txt","r",stdin);
    freopen("test.txt","w",stdout);
    int Case;scanf("%d",&Case);
    for (int C=1;C<=Case;C++){
        printf("Case #%d:\n",C);
        work();
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章