51Nod 1600 Simple Kmp

Solution

考虑这个f(s)的含义,相当于对于每一个后缀而言,他能匹配的前缀的个数(不包含他自己)就是他的深度。反过来看,就是某一个前缀的出现次数。
那么考虑key(s)的含义,就是说对于两个相同的子串s[l1,r1] , s[l2 ,r2],他的贡献就是len - r2。
然后每加入一个点,par树上所对应的链会加上一个endpos,其他的增加一个定值,记录下来,然后就是查询一个链和,完了。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;

struct state{
	int nex[26] , link , len;
}st[N * 2];

int last , scnt , endpos[N];

void init(){scnt = last = 1;}
void extend(int c,int suf){
	int cur = ++scnt , p;endpos[suf] = cur;
	st[cur].len = st[last].len + 1;
	for (p = last ; p && !st[p].nex[c];p = st[p].link) st[p].nex[c] = cur;
	if (!p) { st[cur].link = 1; }
	else{
		int q = st[p].nex[c];
		if (st[q].len == st[p].len + 1){
			st[cur].link = q;
		}else{
			int clone = ++scnt;
			st[clone].len = st[p].len + 1;
			memcpy(st[clone].nex , st[q].nex , sizeof (st[clone].nex) ) ;
			st[clone].link = st[q].link;
			
			st[cur].link = st[q].link = clone;
			
			for (; p && st[p].nex[c] == q;p = st[p].link) st[p].nex[c] = clone;
		}
	}
	last = cur;
}


struct E{
	int to , nex;
}e[N * 2];int head[N * 2] , ecnt;

void adde(int fr,int to){e[++ecnt]=(E){to,head[fr]};head[fr] = ecnt;}
void addedge(int fr,int to){adde(fr,to);adde(to,fr);}

int fa[2 * N] , dfn[N * 2] , tdfn[N * 2] , top[N * 2] , dep[N * 2] , sz[N * 2] , son[N * 2] , clk;

void dfs1(int o){
	sz[o] = 1;
	for (int j = head[o];j;j=e[j].nex){
		dep[e[j].to] = dep[o] + 1;
		fa[e[j].to] = o;
		dfs1(e[j].to);
		sz[o] += sz[e[j].to];
		if (!son[o] || sz[son[o]] < sz[e[j].to]) son[o] = e[j].to; 
	}
}

void dfs2(int o,int Top){
	top[o] = Top;dfn[++clk] = o;tdfn[o] = clk;
	if (son[o]) dfs2(son[o] , Top);
	for (int j = head[o];j;j=e[j].nex)
	if (e[j].to != son[o]){
		dfs2(e[j].to , e[j].to);
	}
}

#define mod 1000000007
int add(int x,int y){
	x += y;
	if (x >= mod) x -= mod;
	return x;
}
struct Node{
	int sum;
	int tg , sumlen;
}tr[N * 8];

void up(int o){
	tr[o].sum = add(tr[o<<1].sum , tr[o<<1|1].sum);
}

void down(int o){
	if (tr[o].tg){
		tr[o<<1].sum = add(tr[o<<1].sum , 1ll * tr[o].tg * tr[o<<1].sumlen % mod);
		tr[o<<1].tg  = add(tr[o<<1].tg ,  tr[o].tg);
		
		tr[o<<1|1].sum = add(tr[o<<1|1].sum , 1ll * tr[o].tg * tr[o<<1|1].sumlen % mod);
		tr[o<<1|1].tg  = add(tr[o<<1|1].tg ,  tr[o].tg);
	
	
		tr[o].tg = 0;
	}
	
}

void build(int o,int l,int r){
	if (l == r){
		tr[o].sumlen = st[dfn[l]].len - st[st[dfn[l]].link].len;
		return;
	}
	int mid = l + r >> 1;
	build(o << 1 , l , mid);
	build(o << 1 | 1 , mid + 1 , r);
	tr[o].sumlen = add(tr[o<<1].sumlen , tr[o<<1|1].sumlen);
}

void modify(int o,int l,int r,int ql,int qr){
	if (ql <= l && r <= qr){
		tr[o].sum = add(tr[o].sum , tr[o].sumlen );
		tr[o].tg = add(tr[o].tg , 1);
		return;
	}
	int mid = l + r >> 1;
	down(o);
	if (ql <= mid) modify(o << 1 , l , mid , ql , qr);
	if (mid < qr ) modify(o << 1 | 1 , mid + 1 , r , ql , qr);
	up(o);
}

int query(int o,int l,int r,int ql,int qr){
	if (ql <= l && r <= qr){
		return tr[o].sum;
	}
	int mid = l + r >> 1 , ans = 0;
	down(o);
	if (ql <= mid) ans += query(o << 1 , l , mid , ql , qr);
	if (mid < qr ) ans += query(o << 1 | 1 , mid + 1 , r , ql , qr);
	return ans;

}
void Modify(int u,int v){
	while (top[u] != top[v]){
		if (dep[top[u]] < dep[top[v]]) swap(u , v);
		modify(1 , 1 , clk , tdfn[top[u]] , tdfn[u]);
		u = fa[top[u]];  
	}
	if (dep[u] > dep[v]) swap(u , v);
	modify(1 , 1 , clk , tdfn[u] , tdfn[v]);
}

int Query(int u,int v){
	int ans = 0;
	while (top[u] != top[v]){
		if (dep[top[u]] < dep[top[v]]) swap(u , v);
		ans = add(ans , query(1 , 1 , clk , tdfn[top[u]] , tdfn[u]) );
		u = fa[top[u]];  
	}
	if (dep[u] > dep[v]) swap(u , v);
	ans = add(ans , query(1 , 1 , clk , tdfn[u] , tdfn[v]) );
	return ans;
}
int n , sum , res;
char s[N];
int main(){
	init();
	cin >> n;
	scanf("%s",s+1);
	for (int i = 1;i <= n;i++) extend(s[i] - 'a' , i);
	for (int i = 2;i <= scnt;i++){
		 adde(st[i].link , i);
	}
	fa[1] = 1;dfs1(1);dfs2(1 , 1);
	build(1 , 1 , clk);
	
	for (int i = 1;i <= n;i++){
		sum = add(sum , Query(1 , endpos[i]));
		res = add(res , sum);
		Modify(1 , endpos[i]);
		printf("%d\n",res);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值