【题目地址】 点击打开链接
【分析】
•这道题首次遇到需要计算每个状态(所代表的子串)在原串中的出现次数,对于点x我们令这个出现次数为x.same(可以理解为有多少串与自己相同)。为了避免重复计算,初始时真节点值为1克隆节点为0。然后沿后缀链接拓扑排序,依拓扑序向前累加x.pre.same+=x.same,即可得到所有节点的same值。
•知道了每个状态的出现次数,用x.same去更新F(x.step)即可。尽管x所表示的子串可能很多,但除了其表示的最长的子串外,用x去更新其它的结果一定不最优。
【代码】
/***************************
ID:Ciocio
LANG:C++
DATE:2014-1-28
TASK:Substrings
***************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define MAXN 250010
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rrep(i,b,a) for(int i=b;i>=a;--i)
struct SAM{
int tot,last;
int pre[MAXN<<1],go[MAXN<<1][26],step[MAXN<<1];
int same[MAXN<<1];
void new_node(int s){
step[++tot]=s;
pre[tot]=0;
same[tot]=0;
memset(go[tot],0,sizeof go[tot]);
}
void build(char* s){
tot=0;last=1;
new_node(0);
int n=strlen(s);
rep(i,0,n-1){
new_node(step[last]+1);
same[tot]=1;
int c=s[i]-'a';
int p=last,np=tot,q,nq;
for(;p&&!go[p][c];p=pre[p]) go[p][c]=np;
if(!p)
pre[np]=1;
else{
q=go[p][c];
if(step[q]==step[p]+1) pre[np]=q;
else{
new_node(step[p]+1);
same[tot]=0;
nq=tot;
memcpy(go[nq],go[q],sizeof go[q]);
pre[nq]=pre[q];
pre[np]=
pre[q]=nq;
for(;p&&go[p][c]==q;p=pre[p]) go[p][c]=nq;
}
}
last=np;
}
}
void get_same(){
queue <int> q;
vector <int> degree(tot+1);
rep(i,1,tot) degree[pre[i]]++;
rrep(i,tot,1)
if(!degree[i])
q.push(i);
while(!q.empty()){
int x=q.front();
q.pop();
if(!--degree[pre[x]]) q.push(pre[x]);
same[pre[x]]+=same[x];
}
}
};
int f[MAXN];
void gmax(int& a,int b){
a=max(a,b);
}
SAM run;
int main(){
static char S[MAXN];
scanf("%s",S);
run.build(S);
run.get_same();
rep(i,1,run.tot)
gmax(f[run.step[i]],run.same[i]);
int n=strlen(S); //如果把strlen(S)写在for里要超时 额
rep(i,1,n)
printf("%d\n",f[i]);
return 0;
}