数字串 [分治+哈希+扩展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 , <mtext>   </mtext> 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

<mtext>                                                     </mtext> . \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ .                                                    .

对于该 平方串, <mstyle mathcolor="purple"> </mstyle> \color{purple}{紫色} 部分应相同, <mstyle mathcolor="brown"> </mstyle> \color{brown}{棕色} 部分应相同.


判断方法

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


l e n 1 = l c s { S 1 [ 1 : S 1 ] , <mtext>   </mtext> S 2 [ m i d : g ] } <mtext>   </mtext> 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 &gt; = S 1 ( 2 n i ) + 1 <mtext>   </mtext> l e n 2 &gt; = m i d len_1&gt;=|S_1|-(2n-i)+1\\ \ \\ len_2 &gt;=mid len1>=S1(2ni)+1 len2>=mid
即可满足 蓝色字体条件.

就说明该串是平方串.

判断部分可以使用 E x _ k m p Ex\_kmp Ex_kmp <mtext>   </mtext> 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));
}

全部评论

相关推荐

08-28 11:37
已编辑
华东师范大学 Java
Sigma777:本来想说师弟怎么把我这个老东西卷没了,仔细一看是师兄 简历不错,但是得准备好选型话术,比如我举个例子你为什么要用caffeine,一般我们的小项目不会有这么hot的key需要本地缓存,你要说明你是如何发现有这么hot的key连redis都兜不住的,引入后优化了多少时间,然后还有本地缓存大小设置为多少,这个大小能保证热点key不会因为太小而淘汰也不会因为太大影响服务吗,为什么不用guava,引入本地缓存同步问题怎么解决。 然后分库分表,为什么你觉得要分表,数据量多少,分多少张表几个库,分片键选择依据,你的所有查询能不能准确定位到某一张避免全库扫描,有没有数据倾斜问题就是分的每张表数据量差距特别大,你是一开始分库分表还是后期发现瓶颈才分,如果后期才分你如何把旧表的数据搬过去同时还能确保业务正常运行。 然后是消息队列,你说缓存高并发请求,却选择了吞吐量较小的rabbit,有什么原因吗,为什么不选Kafka。 然后你说分布式锁解决集群环境并发安全,也就是说你是集群部署的,请问是怎么部署的,docker还是k8s,部署几台,配置是多少,jvm参数设置是多少,有监控吗,线上遇到故障吗,怎么解决的,有做负载均衡吗,数据是怎么压测的等等。 zset缓存本月实时排行数据具体怎么做的,会有大key问题吗。 其他本小渣暂时想不到了,留给其他大神点评
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务