您的位置:首页 > 其它

HDU 3518 Boring counting(后缀自动机 SAM)

2013-03-24 19:09 260 查看
题意:求一个字符串中重复出现两次以上且不重叠的不同字串的个数。比如aaaa中 a和aa符合要求,而aaa和aaaa不符合要求。

思路:好像标程给的是后缀数组,但我是后缀自动机过的,我们只要在后缀自动机中的每一个状态里,设l,r分别表示在这个状态中的子串集合的最左与最右位置,num表示这个状态中的子串一共出现了多少次。

因为在后缀自动机中,每一个状态代表了一类子串,(设一个状态为p)它们的长度范围是[p->par->val+1,p->val],(不妨设为left和right)则如果p->r-p->l>=p->val.则说明这个状态中的每一个字串都符合要求,所以答案要加上right-left+1,如果p->r-p->l处在left和right之间,则说明只有一部分的子串(也就是长度不大于p->r-p->l的子串)符合要求,答案要加上p->r-p->l-left+1,至于left,right和num的更新,由SAM的性质,我们可以先将SAM拓扑排序,然后自底向上更新即可。具体代码如下:

#include <iostream>
#include <string.h>
#include <stdio.h>
#define maxn 2010
#define Smaxn 26
#define mod 2012
#define inf 21000000
using namespace std;
struct node
{
node *par,*go[Smaxn];
int left,right;
int num;
int po;
int val;
}*root,*tail,que[maxn],*top[maxn];
int tot;
char str[maxn>>1];
void add(int c,int l,int po)
{
node *p=tail,*np=&que[tot++];
np->val=l;
np->po=po;
while(p&&p->go[c]==NULL)
p->go[c]=np,p=p->par;
if(p==NULL) np->par=root;
else
{
node *q=p->go[c];
if(p->val+1==q->val) np->par=q;
else
{
node *nq=&que[tot++];
*nq=*q;
nq->val=p->val+1;
np->par=q->par=nq;
while(p&&p->go[c]==q) p->go[c]=nq,p=p->par;
}
}
tail=np;
}
int c[maxn],len;
void init()
{
memset(que,0,sizeof(que));
tot=0;
len=1;
root=tail=&que[tot++];
}
void solve()
{
memset(c,0,sizeof(c));
int i;
for(i=0;i<tot;i++)
c[que[i].val]++;
for(i=1;i<len;i++)
c[i]+=c[i-1];
for(i=0;i<tot;i++)
top[--c[que[i].val]]=&que[i];
for(node *p=root;;p=p->go[str[p->val+1]-'a'])
{
p->num=1;
p->left=p->right=p->po;
if (p->val==len-1)break;
}
for(i=tot-1;i>=0;i--)
{
node *p=top[i];
if(p->left==0&&p->right==0)
{
p->left=p->right=p->po;
}
if(p->par)
{
node *q=p->par;
q->num+=p->num;
if(q->left==0||q->left>p->left)
q->left=p->left;
if(q->right==0||q->right<p->right)
q->right=p->right;
}
}
int ans=0;
for(i=1;i<tot;i++)
{
if(que[i].num>1)
{
int ma=que[i].val,mi=que[i].par->val+1,le=que[i].right-que[i].left;
if(le>=ma)
ans+=(ma-mi+1);
else if(le<ma&&le>=mi)
{
ans+=(le-mi+1);
}
}
}
printf("%d\n",ans);
}
int main()
{
while(1)
{
scanf("%s",str+1);
int i,l=strlen(str+1);
if(str[1]=='#')
break;
init();
for(i=1;i<=l;i++)
{
add(str[i]-'a',len++,i);
}
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: