题目概述
求一个字符串[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;
}