关闭

2016MUTC9-1010 Jong Hyok and String

标签: 2016多校联合训练后缀数组
150人阅读 评论(0) 收藏 举报
分类:

题解:将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;
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:6689次
    • 积分:587
    • 等级:
    • 排名:千里之外
    • 原创:53篇
    • 转载:0篇
    • 译文:2篇
    • 评论:0条