专栏文章
题解:CF2053G Naive String Splits
CF2053G题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miqmhqps
- 此快照首次捕获于
- 2025/12/04 07:13 3 个月前
- 此快照最后确认于
- 2025/12/04 07:13 3 个月前
先不考虑复杂度,就给你两个串,如何判定是否可以拼出 呢?
我们考虑如何正确地直接贪心。假设 不存在公共循环节,原因后面再说。具体地,设我们试图用字符串 拼出字符串 ,且 。此处给定一个贪心:不断地匹配短串 ,直到匹配不上为止,然后尝试匹配一次 。注意到这样可能会出问题,因为如果 有若干个 作为前缀,可能有类似反悔的操作。例如 ,。贪心匹配 时,先用 匹配了 次,然后直接塞 也不合法,但是由于 包含两次 作为前缀,可以撤销掉这两个匹配重新尝试塞 。
形式化地,设 ,我们每次先贪心匹配 ,然后遇到匹配不了的地方先直接匹配 。如果失败了就撤回 次 的匹配,然后接着匹配 。如果再失败,其实如果 是 的前缀,可以再撤回一次 并尝试匹配,具体可以手玩下面这个数据中 的情况:
CPP1
8
abaabaab
abaababa
这个例子里,先匹配了两次 ,然后撤回发现 然后似了。然而再撤回到开头匹配 就能正确。
这启发我们考虑如何反悔。是否需要反悔多次呢?仍设 ,并且先假设有一个串 我们要多反悔两次。这就相当于 。多反悔两次相当于撤回 个 的匹配,即令 。由于 一定是接上 或 ,可以认为 是 的前缀,于是 。可以得到 (注意 必须是 的前缀,长度必然更小)。两个 可以覆盖自己,则它必然和 存在公共循环节,与之前的假设不符。
于是姑且认为只需要多反悔一次即可。
对于 和 有公共循环节的部分,先判断 是否也有这个公共循环节,剩下的部分相当于给定 ,求是否有关于方程 的对于 的非负整数解。
考虑原问题,我们可以直接暴力地做这些操作!注意到对于短串的长度为 ,我们匹配短串的次数最多为 ,而 是调和级数, 可以接受。对于长串,容易发现其出现位置一定不重叠,而长串的长度是 的,于是单次时间复杂度 ,做 次是线性。
对于公共循环节的部分,我们不需要任何 exgcd,直接枚举长串的出现次数判断整除即可,时间复杂度同长串匹配,是线性的。
可以使用 hash 或 Z 实现匹配。时间复杂度 。
关于把暴力匹配改成二分的做法,可以发现用 , 可以卡成 ,所以复杂度理论上还是没啥改进。
这题现在的数据还真是弱爆了,卡不掉二分,卡不掉错误的匹配。从题解看上去像是我造的但是大家其实都不太会造强数据啊,我只是稍微造了点能看的东西吧。不知道后面会不会再加强一下的。
给一个使用二分的代码,在这版数据下跑的挺快。hash 是 SA 改的,所以代码莫名其妙的。
CPP#include<bits/stdc++.h>
using namespace std;
#define base 2310907499
#define MOD 2933256077ll
#define int long long
#define pii pair<int,int>
#define all(v) v.begin(),v.end()
#define pb push_back
#define REP(i,b,e) for(int i=(b);i<(int)(e);++i)
#define over(x) {cout<<(x)<<endl;return;}
#define lowbit(x) ((x)&(-(x)))
#define cntbit(x) __builtin_popcount(x)
#define deal(v) sort(all(v));v.erase(unique(v.begin(),v.end()),v.end())
#define lbound(v,x) lower_bound(all(v),x)-v.begin()
mt19937 sd(random_device{}());
int qpow(int a,int b,int m=MOD,int res=1){
a%=m;
while(b>0)res=(b&1)?(res*a%m):(res),a=a*a%m,b>>=1;
return res;
}
int exgcd(int x,int y,int &a,int &b){
if(y==0){
a=1;b=0;
return x;
}
int d=exgcd(y,x%y,a,b);
int z=b;
b=a-b*(x/y);
a=z;
return d;
}
int _x_,_y_;
inline int inverse(int x,int y=MOD){
exgcd(x,y,_x_,_y_);
return (_x_+y)%y;
}
int TT;
int h[1000];
uniform_int_distribution<int>rd(0,MOD-1);
struct SA{
void initbase(int n=10000000){
p1[0]=p2[0]=1;
p1[1]=base;p2[1]=inverse(base);
REP(i,2,n+1)p1[i]=p1[i-1]*p1[1]%MOD,p2[i]=p2[i-1]*p2[1]%MOD;
}
string s;
int n;
int p1[10000005],p2[10000005];
int sum[10000005];
int ask(int l,int r){
return 1ull*(sum[r+1]+MOD-sum[l])*p2[l]%MOD;
}
void build(string S){
s=S;n=s.size();
sum[0]=0;
REP(i,0,n)sum[i+1]=(sum[i]+p1[i+1]*h[s[i]]%MOD)%MOD;
}
}sa;
int n,m;
bool same(int l,int L,int len){
return sa.ask(l,l+len-1)==sa.ask(L,L+len-1);
}
bool check(int l,int r,int len,int op=1){
if(op)l+=n,r+=n;
if((r-l+1)%len)return 0;
if(r-l+1==len)return 1;
return same(l,l+len,r-l+1-len);
}
int getcopies(int x,int rt,int y,int len){
int l=1,r=(rt-x+1)/len,res=0;
if(!same(x,y,len))return 0;
while(l<=r){
int mid=(l+r)>>1;
if(check(x,x+mid*len-1,len,0))res=mid,l=mid+1;
else r=mid-1;
}
return res;
}
string t,s;
int T;
void Main() {
cin>>m>>n>>s>>t;
T-=clock();
sa.build(t+s);
T+=clock();
int f=m,g=0;
for(int i=m-1;i>=1;--i)if(check(0,m-1,i))f=i;
if(check(0,n-1,f,0)&&same(0,n,f))g=1;
REP(i,0,m-1){
int l1=0,l2=i+1,t1=i+1,t2=m-t1;
if(t1>t2)swap(l1,l2),swap(t1,t2);
if(t1%f==0&&t2%f==0){
if(!g){putchar(48);continue;}
int x=t1/f,y=t2/f,z=n/f,op=0;
for(int i=0;i*y<=z;++i)if((z-i*y)%x==0){op=1;break;}
putchar(op+48);
continue;
}
bool f=1;
int x=0,ct=getcopies(l2+n,l2+t2-1+n,l1+n,t1);
while(x<n){
int l=getcopies(x,n-1,l1+n,t1);
if(x+l*t1==n)break;
else if(l<ct){f=0;break;}
else{
x+=(l-ct)*t1;
if(x+t2<=n&&same(x,l2+n,t2)){x+=t2;continue;}
}
if(l>=ct+1&&x-t1+t2<=n&&same(x-t1,l2+n,t2)){x+=t2-t1;continue;}
f=0;break;
}
putchar(f+48);
}
puts("");
}
void TC() {
sa.initbase();
REP(i,0,200)h[i]=rd(sd);
int tc=1;
cin>>tc;
while(tc--){
Main();
cout.flush();
}
}
signed main() {
return cin.tie(0),cout.tie(0),ios::sync_with_stdio(0),TC(),0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...