【JZOJ 省选模拟】字符串

题目

Description
你喜欢字符串。有人送了你一个仅含小写字母的字符串。
由于你是一名优秀的 𝑂𝐼er,所以你决定对这个字符串展开研究。
定义两个字符串是相似的,当且仅当存在至多一个 𝑖 ,使得这两个字符串中只有第 𝑖 个字母不同。
你取出了这个字符串中所有长度为m 的子串。你想知道,对于每个长度为 m 的子串,有多少个其它长度为 m 的子串与它相似。

Input
第一行两个正整数 n,m,表示字符串长度和你取出的子串长度。

Output
输出一行 n −m + 1 个整数,第 𝑖 个整数表示有多少个串与第 𝑖个串相似(不包括 𝑖 自身)。

Sample Input
【样例输入】
8 3
aabaabab

Sample Output
【样例输出】
2 1 1 2 1 3

Data Constraint

在这里插入图片描述

思路

𝑆, 𝑇 相似,等价于 𝐿𝐶𝑃 𝑆, 𝑇 + 𝐿𝐶𝑆 𝑆, 𝑇 ≥ 𝑚 − 1。
• 考虑建出原串的前缀树和后缀树,每个子串都能分别对应前后缀树上的一个
点,两个串的 LCP 就是这两个串所对应的点在前缀树上的 LCA 的深度,
LCS 同理。
• 那么就是对每个点 𝑢,求有多少个点 𝑣 满足 𝑑𝑒𝑝1 𝑙𝑐𝑎1 𝑢, 𝑣 +
𝑑𝑒𝑝2 𝑙𝑐𝑎2 𝑢, 𝑣 ≥ 𝑚 − 1。

在第一棵树上做 dsu on tree,加入一个点 𝑢 时,可以知道 𝑑𝑒𝑝1 𝑙𝑐𝑎1 𝑢, 𝑣 ,
那么只要求有多少个点满足它与 𝑢 在第二棵树上的 LCA 的深度 ≥ 𝑚 − 1 −
𝑑𝑒𝑝1 𝑙𝑐𝑎1 𝑢, 𝑣 ,就是求 𝑢 往根路径上的深度最小且满足上述要求的点的
子树中,有多少个点在第一棵树中被 dsu 到。
• 考虑用树状数组维护每个点在第二棵树上的 dfs 序,那么它就是一个单点加
区间查。

• 但是这样只能算出有多少对 𝑢, 𝑣 满足 𝑑𝑒𝑝1 𝑙𝑐𝑎1 𝑢, 𝑣 + 𝑑𝑒𝑝2 𝑙𝑐𝑎2 𝑢, 𝑣 ≥
𝑚 − 1。
• 再用一棵树状数组维护当前被 dsu 到的节点的答案,跟上一棵树状数组相反,
这个要支持区间加,单点查。
• 这样就成功解决了本题。
• 复杂度 𝑂(𝑛𝑙𝑜𝑔
2𝑛)。

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 77;

struct S
{
	int l,r,v;
	
	S(){}
	S(int _l,int _r,int _v) :
		l(_l),r(_r),v(_v) {} 
};
int c[N],d[N],son[N],n,m,ans[N],p1[N],p2[N],L,f[N][18],szeA[N],szeB[N],rt;
int lst_y[N],dfn_l,dfn_r,dfnA[N],dfnB[N],timA,timB,real_ans[N];
char s[N];
vector<S>g[N];
vector<int>h[N];

struct SAM
{
	struct point
	{
		int go[26];
	}t[N];
	int maxl[N],fa[N],tot = 1,lst = 1,id[N],anc[N][18],dep[N];
	vector<int>g[N];
	
	void insert(char s)
	{
		int c = s - 'a',i = lst;
		lst = ++tot;
		maxl[lst] = maxl[i] + 1;
		for(; i && !t[i].go[c]; i = fa[i]) t[i].go[c] = lst;
		if(!i) fa[lst] = 1;
		else
		{
			int j = t[i].go[c];
			if(maxl[j] == maxl[i] + 1) fa[lst] = j;
			else
			{
				int p;
				t[p = ++tot] = t[j];
				fa[p] = fa[j];
				fa[j] = fa[lst] = p;
				maxl[p] = maxl[i] + 1;
				for(; i && t[i].go[c] == j; i = fa[i]) t[i].go[c] = p;
			}
		} 
	}
	
	void build()
	{
		for(int i = 2; i <= tot; i++) g[fa[i]].push_back(i);
	}
}A,B;

void change(int *c,int x,int v)
{
	for(int i = x; i <= L; i += i & -i) c[i] += v;
}

int query(int *c,int x)
{
	int res = 0;
	for(int i = x; i; i -= i & -i) res += c[i];
	return res;
}

void dfsA(int u)
{
	dfnA[u] = ++timA;
	szeA[u] = 1;
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++)
	{
		int v = A.g[u][i];
		dfsA(v);
		szeA[u] += szeA[v];
		if(szeA[v] > szeA[son[u]]) son[u] = v;
	}
}

void dfsB(int u)
{
	dfnB[u] = ++timB;
	szeB[u] = 1;
	int len = B.g[u].size(),i;
	for(i = 0; i < 17; i++) f[u][i + 1] = f[f[u][i]][i];
	for(i = 0; i < len; i++)
	{
		int v = B.g[u][i];
		f[v][0] = u;
		dfsB(v);
		szeB[u] += szeB[v];
	}
}

int jumpB(int x,int d)
{
	for(int i = 17; i >= 0; i--)
	if(f[x][i] && B.maxl[f[x][i]] >= d) x = f[x][i];
	return x;
}

void add(int u)
{
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos];
	change(c,dfnB[z],1);
}

void del(int u)
{
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos];
	change(c,dfnB[z],-1);
}

void ask(int u,int x)
{
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos],y = jumpB(z,m - 1 - A.maxl[x]);
	lst_y[u] = y;
	ans[pos] += query(c,dfnB[y] + szeB[y] - 1) - query(c,dfnB[y] - 1);
}

void calc_son(int u)
{
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos],y = lst_y[u];
	ans[pos] += query(d,dfnB[y] + szeB[y] - 1) - query(d,dfnB[y] - 1);
}

void ins(int u)
{
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos];
	change(d,dfnB[z],1);
}

void clear(int u)
{
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos];
	change(d,dfnB[z],-1);
}

void join(int u)
{
	if(!A.id[u]) return;
	int y = lst_y[u];
	g[dfn_l].push_back(S(dfnB[y],dfnB[y] + szeB[y] - 1,1));
	g[dfn_r + 1].push_back(S(dfnB[y],dfnB[y] + szeB[y] - 1,-1));
}

void dfs_add(int u)
{
	add(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_add(A.g[u][i]);
}

void dfs_del(int u)
{
	del(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_del(A.g[u][i]);
}

void dfs_ask(int u,int x)
{
	ask(u,x);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_ask(A.g[u][i],x); 
}

void dfs_cs(int u)
{
	calc_son(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_cs(A.g[u][i]); 
}

void dfs_ins(int u)
{
	ins(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_ins(A.g[u][i]);
}

void dfs_clear(int u)
{
	clear(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_clear(A.g[u][i]);
}

void dfs_join(int u)
{
	join(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_join(A.g[u][i]);
}

void dfsC(int u,bool op)
{
	int i,len = A.g[u].size();
	for(i = 0; i < len; i++)
	{
		int v = A.g[u][i];
		if(v != son[u]) dfsC(v,0);
	}
	if(son[u]) dfsC(son[u],1);
	
	ask(u,u); add(u);
	for(i = 0; i < len; i++)
	{
		int v = A.g[u][i];
		if(v != son[u])
		{
			dfs_ask(v,u);
			dfs_add(v);
		}
	}
	del(u);
	for(i = 0; i < len; i++)
	{
		int v = A.g[u][i];
		if(v != son[u]) dfs_del(v);
	}
	
	for(i = len - 1; i >= 0; i--)
	{
		int v = A.g[u][i];
		if(v != son[u])
		{
			dfs_ask(v,u);
			dfs_add(v);
		}
	}
	ask(u,u);
	for(i = 0; i < len; i++)
	{
		int v = A.g[u][i];
		if(v != son[u]) dfs_del(v);
	}
	
	calc_son(u);
	for(i = 0; i < len; i++)
	{
		int v = A.g[u][i];
		if(v != son[u]) dfs_cs(v);
	}
	if(op)
	{
		ins(u);
		for(i = 0; i < len; i++)
		{
			int v = A.g[u][i];
			if(v != son[u]) dfs_ins(v);
		}
	}
	else if(son[u]) dfs_clear(son[u]);
	
	if(son[u])
	{
		dfn_l = dfnA[son[u]]; 
		dfn_r = dfn_l + szeA[son[u]] - 1;
		join(u);
		for(i = 0; i < len; i++)
		{
			int v = A.g[u][i];
			if(v != son[u]) dfs_join(v);
		}
	}
}

int main()
{
	freopen("string.in","r",stdin); freopen("string.out","w",stdout);
	scanf("%d%d",&n,&m);
	int i,j;
	scanf("%s",s + 1);
	for(i = 1; i <= n; i++)
	{
		A.insert(s[i]);
		if(i >= m) A.id[A.lst] = i - m + 1,p1[i - m + 1] = A.lst;
	}
	for(i = n; i >= 1; i--)
	{
		B.insert(s[i]);
		if(i + m - 1 <= n) B.id[B.lst] = i,p2[i] = B.lst;
	}
	A.build(); B.build(); L = B.tot;
	dfsA(1); dfsB(1); dfsC(1,0);
	memset(c,0,sizeof(c));
	for(i = 1; i <= n - m + 1; i++) h[dfnA[p1[i]]].push_back(i);
	for(i = 1; i <= A.tot; i++)
	{
		int leng = g[i].size();
		for(j = 0; j < leng; j++)
		{
			S tmp = g[i][j];
			change(c,tmp.l,tmp.v);
			change(c,tmp.r + 1,-tmp.v);
		}
		int lenh = h[i].size();
		for(j = 0; j < lenh; j++)
		{
			int x = h[i][j];
			ans[x] += query(c,dfnB[p2[x]]);
		}
	}
	for(i = 1; i <= n - m + 1; i++)
	{
		printf("%d ",ans[i]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值