【后缀自动机】HDU4622[Reincarnation]题解

题目概述

求一个字符串[L,R]中不同子串的个数。

解题报告

又是一道SAM的经典应用,不过同样的还是需要转化。
我们发现一个状态s会father(s)多出一些子串,但是多了哪些呢?看图:
这里写图片描述
从图中可以比较明显的看出多出的部分就是s->MAX-father(s)->MAX,而每次插入并不会引起大规模变化,所以我们一边扩展,一边修正子串个数tot就行了。
这道题是多次询问,所以我们构造n次SAM预处理答案就可以了。

示例程序

#include<cstdio>
#include<cstring>
using namespace std;
const int maxl=2000;

int te,len,Q,ans[maxl+5][maxl+5];
char s[maxl+5];
struct SAM
{
    static const int maxi=26;
    struct node
    {
        node *son[maxi],*fa;int MAX;
        int Count() {if (!fa) return 0;return MAX-fa->MAX;}
        //计算多出来的子串个数
    };
    typedef node* P_node;
    node tem[2*maxl+5];P_node pos,ro,lst;
    int tot;
    P_node newnode(int M)
    {
        pos->MAX=M;pos->fa=0;memset(pos->son,0,sizeof(pos->son));
        return pos++;
    }
    void clear() {pos=tem;ro=newnode(0);lst=ro;tot=0;}
    int Insert(char ch)
    {
        int ID=ch-'a';P_node p=lst,np=newnode(p->MAX+1);
        while (p&&!p->son[ID]) p->son[ID]=np,p=p->fa;
        if (!p) np->fa=ro,tot+=np->Count(); else //更新
        {
            P_node q=p->son[ID];
            if (p->MAX+1==q->MAX) np->fa=q,tot+=np->Count(); else //更新
            {
                P_node nq=newnode(p->MAX+1);
                memcpy(nq->son,q->son,sizeof(q->son));
                tot-=q->Count(); //q的father要改变了,先减去原个数
                nq->fa=q->fa;q->fa=nq;np->fa=nq;
                tot+=q->Count()+np->Count()+nq->Count(); //更新,加上q新的个数
                while (p&&p->son[ID]==q) p->son[ID]=nq,p=p->fa;
            }
        }
        lst=np;return tot;
    }
};
SAM sam;

bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
char readc()
{
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; else return *l++;
}
int readi(int &x)
{
    int tot=0,f=1;char ch=readc(),lst=ch;
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=readc();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=readc();
    x=tot*f;
    return Eoln(ch);
}
int reads(char *s)
{
    int len=0;char ch=readc();if (ch==EOF) return EOF;
    while ('z'<ch||ch<'a') ch=readc();s[++len]=ch;
    while ('a'<=s[len]&&s[len]<='z') s[++len]=readc();s[len--]=0;
    return len;
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    readi(te);
    while (te--)
    {
        len=reads(s);readi(Q);
        for (int i=1;i<=len;i++)
        {
            sam.clear();
            for (int j=i;j<=len;j++)
                ans[i][j]=sam.Insert(s[j]);
        }
        while (Q--) {int x,y;readi(x);readi(y);printf("%d\n",ans[x][y]);}
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值