字符串作业(三)

本文介绍了多个关于字符串处理的编程竞赛题目,包括LOJ #6158的末尾0最多的加法问题,使用exKMP算法;CF914F的单点修改求子串出现次数,利用bitset优化;CF1131E的字符串乘法,维护最长相同字母连续子序列;CF653F的括号子串计数,通过SAM结构;CF610E的字母排列查询,线段树维护;CF741E的DNA问题,通过后缀排序和RMQ解决;CF862F的LCP最大乘积,维护相邻字符串LCP;CF587F的字符串分块查询,AC自动机应用;以及CF1110H和LOJ #6681的树上回文串问题,使用AC自动机和点分治。文章涵盖了字符串处理的各种高效算法和数据结构应用。
摘要由CSDN通过智能技术生成

LOJ #6158. A + B Problem

给出一个数字,求在其中两位之间插入一个加号后得到的答案末尾 0 0 0最多的个数。

题解:
第二个数的末尾就是原串的末尾,所以如果需要和第一个数加起来末尾为 0 0 0,那么从后往前扫,第二个数为 0 0 0的位在不进位的情况下第一个数的对应位需要是 0 0 0,不为 0 0 0的位的对应位 x x x应该是 10 − x 10 - x 10x,然后开启进位模式所有数的对应位都是 9 − x 9-x 9x,然后求一个原串和转换后的串的最长公共前缀即可得出答案,可以写 e x k m p \rm exkmp exkmp
A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 1000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 998244353
#define S 131
#define LL long long
using namespace std;

char s[maxn],s0[maxn];
LL hs[2][maxn],pw[maxn];
int n;

LL calc(int a,int b,LL *hs){
    return ((hs[b] - hs[a-1] * pw[b-a+1]) % mod + mod) % mod; }

int main(){
   
	pw[0] = 1;
	rep(i,1,maxn-1) pw[i] = pw[i-1] * S % mod;
	while(scanf("%s",s+1)!=EOF){
   
		n = strlen(s+1);
		rep(i,1,n) hs[0][i] = (hs[0][i-1] * S + s[i]) % mod , s0[i] = s[i]; 
		int i;
		for(i=n;i>=1 && s[i] == '0';i--);
		s[i] = (10 - (s[i] - '0')) + '0';
		int t = i;
		per(j,i-1,1) s[j] = (9 - (s[j] - '0')) + '0';
		rep(i,1,n) hs[1][i] = (hs[1][i-1] * S + s[i]) % mod;
		int ans = 0;
		rep(i,1,n-1){
   
			int L = 0 , R = min(i , n-i) , mid;
			for(;L<R;){
   
				mid = L+R+1 >> 1;
				if(calc(i-mid+1,i,hs[0]) == calc(n-mid+1,n,hs[1]))
					L = mid;
				else 
					R = mid - 1;
			}
			if(i <= t){
   
				while(n-L <= i && s0[i-L] == '9') L++;
				while(i-L <= 0 && n-L > i && s0[n-L] == '9') L++;
			}
			else{
   
				while(n-L <= i && s0[i-L] == '0') L++;
				while(i-L <= 0 && n-L > i &&s0[n-L] == '0') L++;
			}
			ans = max(ans , L);
		}
		printf("%d\n",ans);
	}
}

CF914F Substrings in a String

题意:单点修改 S S S中的一个字符,求 S [ l . . . r ] S[l...r] S[l...r] T T T的出现次数, ∣ S ∣ , ∑ ∣ T ∣ ≤ 1 e 5 , T i m e l i m i t   6 s |S| , \sum|T| \leq 1e5,\rm Timelimit \ 6s S,T1e5Timelimit 6s

太离谱了。
考虑到这是 C F \rm CF CF 6 s \rm 6s 6s,我们用 b i t s e t   C [ i ] [ j ] \rm bitset\ C[i][j] bitset C[i][j]代表字符 i i i在位置 j j j是否出现。
则求出 r e t = and ⁡ i = 0 ∣ T ∣ − 1 C [ T i ] > > i ret = \operatorname{and}_{i=0}^{|T|-1} C[T_i]>>i ret=andi=0T1C[Ti]>>i后取 r e t ret ret [ l , r − ∣ T ∣ + 1 ] [l,r-|T|+1] [l,rT+1]中的 1 1 1的个数即可。
时间复杂度 O ( n 2 ω ) O(\frac {n^2}{\omega}) O(ωn2)无压力水过
其实可以分块一下得到更科学的复杂度:
对于长度大于块大小 S S S的字符串我们暴力 k m p \rm kmp kmp, O ( n 2 S ) O(\frac {n^2}S) O(Sn2)
对于长度小于块大小 S S S的字符串我们分它是在一个块内还是跨两个块来考虑。
对于块内的答案直接分块求块内的 b i t s e t \rm bitset bitset即可做到 O ( n S ω ) O(\frac {nS}{\omega}) O(ωnS)
对于跨两个块的答案直接对于 n S \frac nS Sn个交界处前后 ∣ T ∣ |T| T个字符拿出来跑 k m p \rm kmp kmp O ( n ∣ T ∣ S ) O(\frac {n|T|}S) O(SnT),总复杂度还是 O ( n 2 S ) O(\frac {n^2}S) O(Sn2)
所以 S = ω n S = \sqrt {\omega n} S=ωn 时最快,为 O ( n n ω ) O(\frac {n\sqrt n}{\sqrt \omega}) O(ω nn )

A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;

char s[maxn],ch[maxn];
int Q,n;

bitset<maxn>C[26];

int main(){
   
	scanf("%s",s+1);
	n = strlen(s+1);
	rep(i,1,n) C[s[i] - 'a'][i] = 1;
	scanf("%d",&Q);
	for(int op,l,r;Q--;){
   
		scanf("%d%d",&op,&l);
		if(op == 1){
   
			scanf("%s",ch);
			C[s[l]-'a'][l] = 0;
			C[(s[l]=ch[0])-'a'][l] = 1;
		}
		else{
   
			scanf("%d%s",&r,ch);
			int t = strlen(ch);
			static bitset<maxn>ans;
			ans.set();
			rep(i,0,t-1)
				ans &= (C[ch[i] - 'a'] >> i);
			r -= t - 1;
			ans <<= maxn - 1 - r;
			ans >>= maxn - 1 - r + l;
			printf("%d\n",ans.count());
		}
	}
}

O ( n n w ) O(\frac {n\sqrt n}{\sqrt w}) O(w nn )做法:(实际上没有快多少)

#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define S 1800
using namespace std;

char s[maxn],ch[maxn];
int Q,n,id[maxn],st[maxn],ed[maxn],nxt[maxn],m;

bitset<S>C[26][maxn/S+5];

int kmp(int l,int r){
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值