数字串 [分治+哈希+扩展KMP]


ViolenceViolence :

  • 枚举子串, 复杂度 O(N2)O(N^2), 再O(N)O(N)判断是否对答案做贡献.
    总复杂度 O(N3)O(N^3).

SolutionSolution :

这道题目要求 快速地求出所有平方串的价值和,

首先整体使用分治处理.

假设现在整个串分为 22 个串 S1, S2S_1, S_2 ;
22 个串拼起来时会有新的 平方串 产生, 新的平方串分为 22 种情况:

  1. midmidS1S_1 上 .
  2. midmidS2S_2 上 .
  3. midmidS1S_1, S2S_2 中间 .

以第二种情况为例分析 :

设当前 枚举的平方串长度2n2n, 终点为 ii,
i[n,2n1]i ∈ [n, 2n-1] ,
则在 S1S_1 处的起点为 t=S1(2ni)+1t = |S_1|-(2n-i)+1 .
g=imid+1g = i-mid+1

                                                   . .

对于该 平方串, color{purple}{紫色} 部分应相同, color{brown}{棕色} 部分应相同.


判断方法

lcslcs 为最长公共后缀
lcplcp 为最长公共前缀


len1=lcs{S1[1:S1], S2[mid:g]} len2=lcp{S2[1,mid],S2[g,i]}len_1 = lcs{S_1[1:|S_1|], S_2[mid:g]}\ \ len_2 = lcp{S_2[1,mid],S_2[g,i]}
则只需要满足
len1>=S1(2ni)+1 len2>=midlen_1>=|S_1|-(2n-i)+1\ \ len_2 >=mid
即可满足 蓝色字体条件.

就说明该串是平方串.

判断部分可以使用 Ex_kmpEx\_kmp  O(S1+S2) O(|S_1|+|S_2|) 完成.
配合 分治, 总的复杂度为 O(nlogn)O(n*logn)


CodeCode

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define SZ 1234567
const int MOD=666623333;
char*s,*t; int n,m,nx[SZ],ex[SZ];
void exkmp(bool g=0) {
	nx[1]=m; int a=0,p=0;
	for(int i=2;i<=m;++i) {
		int j=nx[i-a+1];
		if(i+j>p) for(j=max(0,p-i);i+j<=m&&t[i+j]==t[j+1];j++);
		nx[i]=j; if(i+j-1>p) a=i,p=i+j-1;
	}
	if(g) return;
	a=p=0;
	for(int i=1;i<=n;++i) {
		int j=nx[i-a+1];
		if(i+j>p) for(j=max(0,p-i+1);i+j<=n&&j<m&&s[i+j]==t[j+1];j++);
		ex[i]=j; if(i+j-1>p) a=i,p=i+j-1;
	}
}
char t1[SZ],t2[SZ];
int N,a[SZ],b[SZ]; char str[SZ];
ll h[SZ],q_[SZ],*q=q_+2,p10[SZ];
template<class T>
void ri(char*s1,int l1,char*s2,int l2,int g,int r,T op) {
	--s1; --s2;
	memcpy(t1,s1,sizeof(char)*(l1+1));
	memcpy(t2,s2,sizeof(char)*(l2+1));
	if(r) reverse(t1+1,t1+1+l1), reverse(t2+1,t2+1+l2);
	t=t2; m=l2; exkmp(1);
	for(int i=1;i<=l2;++i) b[i]=nx[i]; b[m+1]=0;
	reverse(t1+1,t1+1+l1); reverse(t2+1,t2+1+l2);
	s=t2; t=t1; n=l2; m=l1; exkmp();
	for(int i=1;i<=l2;++i) a[i]=ex[l2+1-i];
	for(int i=1;i<=l2;++i) {
		int L=i+i-a[i],R=b[i+1]+i;
		L=max(L,i+g); R=min(R,i+i-1); R=min(R,l2);
		if(L<=R) op(L,R,i);
	}
}
ll ans=0;
void op(int l,int r,int p) {
	ll hfs=q[r-p]-q[l-p-1];
	hfs-=p10[p]*(q[r-p-p]-q[l-p-p-1])%MOD;
	hfs=hfs%MOD*(p10[p]+1)%MOD; (ans+=hfs)%=MOD;
}
int padd=0;
inline void o1(int a,int b,int c){
        op(a+padd,b+padd,c);
}
inline void o2(int a,int b,int c){
        op(padd-b+c+c-1,padd-a+c+c-1,c);
}
void work(int l,int r) {
	if(l==r) return; 
	int m=(l+r)>>1;     
	work(l,m); work(m+1,r);
	padd=m; ri(str+l,m-l+1,str+m+1,r-m,0,0,o1);
	padd=m+1; ri(str+m+1,r-m,str+l,m-l+1,1,1,o2);
}
// {{{ KSM
ll qp(ll a,ll b) {
	ll x=1; a%=MOD;
	while(b) {
		if(b&1) x=x*a%MOD;
		a=a*a%MOD; b>>=1;
	}
	return x;
}
// }}}
int main(){
	FO(num) 
	p10[0]=1;
        for(int i=1;i<SZ;++i) p10[i]=p10[i-1]*10%MOD; // Get 10^i
	scanf("%s",str+1); N=strlen(str+1);
	for(int i=1;i<=N;++i) h[i]=(h[i-1]*10+str[i]-48)%MOD; // Hash
	for(int i=1;i<=N;++i)
		q[i]=(q[i-1]+((str[i+1]-'0')?h[i]:0))%MOD;    // Hash
	work(1,N); ll fm=N*(ll)(N+1)/2; fm%=MOD;
	ans=ans*qp(fm,MOD-2)%MOD; ans=(ans%MOD+MOD)%MOD;
	printf("%d
",int(ans));
}

原文地址:https://www.cnblogs.com/zbr162/p/11822580.html