JZOJ 4669 弄提纲

题目大意

给出一个字符串S和m个询问。
每个询问有两个数l,r,要求以结尾为第l位和第r位的子串中,有多少个是原串的前缀,以及当中最长的子串的长度。

|S|<=30000,m<=100000
时间限制 1s
空间限制 256M

解题思路

先做一遍kmp得到数组P,然后令字符串的每一位对应一个点,构出一棵树,满足P[i]是i的父亲,这样最长长度就是l、r的lca的编号,子串数量就是lca的深度。

#include
#include
#include
#define maxn 30006
#define fr(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll ding=1e8+7;

struct poi
{
    int x;
    poi *nex;
} *a[maxn];
int i,n,len,l,r,s,ans,p[maxn],dep[maxn],fa[maxn][27];
char st[maxn];
void link(int x,int y)
{
    poi *p=new poi;
    p->x=y;
    p->nex=a[x];
    a[x]=p;
    return;
}
void dfs(int x)
{
    poi *p=new poi;
    for(p=a[x];p;p=p->nex)
        if (p->x!=fa[x][0])
        {
            dep[p->x]=dep[x]+1;
            fa[p->x][0]=x;
            dfs(p->x);
        }
    return;
}
void pre()
{
    int i,j=0;
    p[1]=0;
    len=strlen(st+1);
    fr(i,2,len)
    {
        while (j && st[i]!=st[j+1]) j=p[j];
        if (st[i]==st[j+1]) j++;
        p[i]=j;
    }
    fr(i,1,len) link(i,p[i]),link(p[i],i);
    dfs(0);
    fr(j,1,26)
        fr(i,1,len)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    return;
}
int getlca(int x,int y)
{
    if (dep[y]>dep[x]) swap(x,y);
    int i,d=dep[x]-dep[y];
    fr(i,0,26)
        if (d & (1 << i))
            x=fa[x][i];
    if (x==y) return x;
    for(i=26;i>=0;i--)
        if (fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int main()
{
    scanf("%s",st+1);
    pre();
    scanf("%d",&n);
    fr(i,1,n)
    {
        scanf("%d%d",&l,&r);
        int lca=getlca(l,r);
        printf("%d %d\n",dep[lca],lca);
    }
    return 0;
}

你可能感兴趣的:(挖坑记,字符串,kmp)