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


V i o l e n c e Violence Violence :

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

S o l u t i o n Solution Solution :

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

首先整体使用分治处理.

假设现在整个串分为 2 2 2 个串 S 1 ,   S 2 S_1,\ S_2 S1, S2 ;
2 2 2 个串拼起来时会有新的 平方串 产生, 新的平方串分为 2 2 2 种情况:

  1. m i d mid mid S 1 S_1 S1 上 .
  2. m i d mid mid S 2 S_2 S2 上 .
  3. m i d mid mid S 1 S_1 S1, S 2 S_2 S2 中间 .

以第二种情况为例分析 :

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

                                                    . \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ .                                                    .

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


判断方法

l c s lcs lcs 为最长公共后缀
l c p lcp lcp 为最长公共前缀

设 设
l e n 1 = l c s { S 1 [ 1 : ∣ S 1 ∣ ] ,   S 2 [ m i d : g ] }   l e n 2 = l c p { S 2 [ 1 , m i d ] , S 2 [ 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=lcs{S1[1:S1], S2[mid:g]} len2=lcp{S2[1,mid],S2[g,i]}
则只需要满足
l e n 1 > = ∣ S 1 ∣ − ( 2 n − i ) + 1   l e n 2 > = m i d len_1>=|S_1|-(2n-i)+1\\ \ \\ len_2 >=mid len1>=S1(2ni)+1 len2>=mid
即可满足 蓝色字体条件.

就说明该串是平方串.

判断部分可以使用 E x _ k m p Ex\_kmp Ex_kmp   O ( ∣ S 1 ∣ + ∣ S 2 ∣ ) \ O(|S_1|+|S_2|)  O(S1+S2) 完成.
配合 分治, 总的复杂度为 O ( n ∗ l o g n ) O(n*logn) O(nlogn)


C o d e Code Code

#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\n",int(ans));
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值