【JZOJ6379】小w与密码(password)

description

【JZOJ6379】小w与密码(password)_第1张图片


analysis

  • 考虑 s s s的某个前缀 a a a t t t的某个前缀 b b b拼成的一个字符串 a + b a+b a+b

  • 如果该字符串可以被另几种 a ′ + b ′ a'+b' a+b拼出来,钦定 b b b最短的那种只算一次

  • 考虑 a 0 + b 0 = a 1 + b 1 = . . . = a k + b k a_0+b_0=a_1+b_1=...=a_k+b_k a0+b0=a1+b1=...=ak+bk,其中 b 0 b_0 b0最短

  • 其中满足 b i b_i bi同时是 b i + 1 b_{i+1} bi+1的前缀和后缀,可以画图理解

  • 这是因为每个 b b b都是 t t t的前缀,且 b i b_i bi也完全是 b i + 1 b_{i+1} bi+1的后缀

  • 相当于 K M P KMP KMP求得的 n e x t next next的定义, b i b_i bi n e x t next next总指向 b i − 1 b_{i-1} bi1的末尾位置

  • 对于 t t t的前缀,只要该前缀有 n e x t next next,说明这种前缀可以被它指向的 n e x t next next的更短前缀替代

  • 当然前缀还可以被它自己的 n e x t next next不断替代,直到该前缀没有 n e x t next next,即不可被替代

  • 建出 t t t n e x t next next后,拿 s s s t t t上面跑 K M P KMP KMP,求出 t t t每个前缀在 s s s中出现的次数 n u m num num

  • 具体求就是匹配完把 n u m num num求一次后缀和,相当于所有情况全部集中到没有 n e x t next next的前缀上,就求出了每个前缀出现次数

  • 然后把答案减掉前缀出现的次数就好了

  • 这题思维难但代码很短,以后要好好动脑想


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include
#include
#include
#define MAXN 100005
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)

using namespace std;

ll next[MAXN],num[MAXN];
char s[MAXN],t[MAXN];
ll n,m,ans;

int main()
{
	//freopen("T2.in","r",stdin);
	freopen("password.in","r",stdin);
	freopen("password.out","w",stdout);
	scanf("%s%s",s+1,t+1),n=strlen(s+1),m=strlen(t+1),ans=n*m;
	for (reg i=2,j=0;i<=m;++i)
	{
		while (j && t[j+1]!=t[i])j=next[j];
		if (t[j+1]==t[i])++j;next[i]=j;
	}
	for (reg i=2,j=0;i<=n;++i)
	{
		while (j && t[j+1]!=s[i])j=next[j];
		if (t[j+1]==s[i])
		{
			++num[++j];
			if (j==m)j=next[j];
		}
	}
	fd(i,n,1)num[next[i]]+=num[i];
	fo(i,1,m)ans-=next[i]?num[i-next[i]]:0;
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(模拟赛,KMP)