• 【CF235C】Cyclical Quest


    题目

    题目链接:https://codeforces.com/problemset/problem/235/C
    给定一个主串 (S)(n) 个询问串,求每个询问串的所有循环同构在主串中出现的次数总和。
    (nleq 10^5,|S|,sum|T|leq 10^6)

    思路

    考虑字符串 (abcde)(a) 移动到最后一位时,实际上就是先将第一位删除,再添加一个新的字符。
    而我们知道 SAM 的 parent 树的父子关系为在字符串前添加若干个字符,自动机上两个相邻的点等价于在字符串后面加上一个字符。所以我们考虑用 SAM 解决此题。
    我们先枚举 (T) 的每一位,如果当前节点存在一个儿子为这个字符,那么就跳到这个字符;否则不断往 parent 树上的父亲跳。
    同时维护一个变量 (cnt),表示当前字符串与自动机上的子串匹配了多少位。
    然后再次枚举 (T),考虑把当前的字符移动到最后。如果此时 (cnt) 等于询问串的长度,那么显然删除后匹配的位数会减一,因为被我们删除的第一位本来是匹配的。
    然后因为我们删除了一个字符,我们需要判断它的 (mathrm{endpos}) 集合有没有改变(也就是匹配的长度是否等于了当前节点的父节点的长度),如果有,那么就往它的父节点跳。
    接下来重复寻找下一个含有字符 (c) 的过程,然后继续维护好 (cnt) 即可。如果一次移动之后 (cnt) 等于询问串长度,那么就要加上位置不同的子串数量。这个很好预处理。
    注意因为循环同构要求互不相同,所以我们要在 SAM 上遍历过的节点打上 tag。
    时间复杂度 (O(n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=2000010;
    int Q,n,m,vis[N],b[N],a[N];
    char s[N],t[N];
    
    struct SAM
    {
    	int last,tot,fa[N],ch[N][26],siz[N],len[N];
    	SAM() { last=tot=1; }
    	
    	void ins(int c)
    	{
    		int p=last,np=++tot; 
    		last=np; len[np]=len[p]+1; siz[np]=1;
    		for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
    		if (!p) fa[np]=1;
    		else
    		{
    			int q=ch[p][c];
    			if (len[q]==len[p]+1) fa[np]=q;
    			else
    			{
    				int nq=++tot;
    				len[nq]=len[p]+1; fa[nq]=fa[q];
    				for (int i=0;i<26;i++) ch[nq][i]=ch[q][i];
    				fa[q]=fa[np]=nq;
    				for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
    			}
    		}
    	}
    	
    	void topsort()
    	{
    		for (int i=1;i<=tot;i++) b[len[i]]++;
    		for (int i=1;i<=tot;i++) b[i]+=b[i-1];
    		for (int i=1;i<=tot;i++) a[b[len[i]]--]=i;
    		for (int i=tot;i>=1;i--) siz[fa[a[i]]]+=siz[a[i]];
    	}
    	
    	void solve(char *s)
    	{
    		m=strlen(s+1);
    		int cnt=0,p=1,ans=0;
    		for (int i=1;i<=m;i++)
    		{
    			int id=s[i]-'a';
    			while (p && !ch[p][id])
    				p=fa[p],cnt=len[p];
    			if (!p) p=1,cnt=0;
    			else cnt++,p=ch[p][id];
    		}
    		if (cnt==m)
    		{
    			cnt--;
    			if (fa[p] && len[fa[p]]==cnt) p=fa[p];
    		}
    		for (int i=1;i<=m;i++)
    		{
    			int id=s[i]-'a';
    			while (p && !ch[p][id])
    				p=fa[p],cnt=len[p];
    			if (!p) p=1,cnt=0;
    			else
    			{
    				cnt++; p=ch[p][id];
    				if (cnt==m)
    				{
    					if (vis[p]>Q) vis[p]=Q,ans+=siz[p];
    					cnt--;
    					if (fa[p] && len[fa[p]]==cnt) p=fa[p];
    				}
    			}
    		}
    		printf("%d
    ",ans);
    	}
    }sam;
    
    int main()
    {
    	scanf("%s%d",s+1,&Q);
    	n=strlen(s+1);
    	for (int i=1;i<=n;i++)
    		sam.ins(s[i]-'a');
    	sam.topsort();
    	memset(vis,0x3f3f3f3f,sizeof(vis));
    	while (Q--)
    	{
    		scanf("%s",t+1);
    		sam.solve(t);
    	}
    	return 0;
    }
    
  • 相关阅读:
    delphi 静态3维数组。 严重占用堆栈 切记。 应该用动态数组, 非要用静态数组的话, 要在编译器里 把 堆栈 调大
    Softmax 函数的特点和作用是什么?
    笔记本按开机键没反应怎么办
    小鸡 模拟器 支持双手柄。
    windows7 玩 WinKawaks kof2002为什么提示couldn't initialise DirectSound?
    Delphi中堆栈区别
    最让人纠结的等式:0.999...=1
    求 主板型号 945GME
    电脑可以识别sd卡手机无法识别 的解决方法。 我成功了。 淘宝买的sd卡 不用退货了。 退的人肝疼
    【线段树】HDU 5443 The Water Problem
  • 原文地址:https://www.cnblogs.com/stoorz/p/14254655.html
Copyright © 2020-2023  润新知