CF314B Sereja and Periods
这里介绍两种做法,感觉都很巧妙。
Solution 1
观察题面,发现 \(|a|,|c|\leq 100\) ,所以可以像 \(KMP\) 那样,造一个 \(nxt\) 数组,然后再弄一个 \(cnt\) 数组。
其中 \(cnt_i\) 表示从 \(c_i\) 开始匹配,把 \(a\) 串扫一遍有几组匹配, \(nxt_i\) 表示 \(c_i\) 匹配完一个 \(a\) 到了 \(c\) 的哪一位。
然后从 \(1\) 到 \(b\) ,每次跳 \(nxt\) , \(ans+=cnt\) 即可。
时间复杂度: \(O(len_a\cdot len_b)+O(b)=O(b)\)
代码1
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=110;
ll cnt[N],nxt[N],b,d,lena,lenc,ans;
char a[N],c[N];
int main(){
scanf("%lld%lld%s%s",&b,&d,a,c);
lena=strlen(a),lenc=strlen(c);
for(int i=0,x;i<lenc;i++){
x=i;
for(int j=0;j<lena;j++)
if(a[j]==c[x]){
x++;
if(x==lenc) cnt[i]++,x=0;
}
nxt[i]=x;
}
int pos=0;
for(int i=1;i<=b;i++){
ans+=cnt[pos];
pos=nxt[pos];
}
printf("%lld\n",ans/d);
return 0;
}
Solution2
我们用倍增的思想来做题。
弄一个倍增数组 \(go\) ,拿 \(pair\) 储存。
设 \(go_{k,idx(i,j)}\) 为从 \(a\) 的第 \(i\) 位, \(c\) 的第 \(j\) 位开始匹配,匹配 \(c\) 的 \(2^k\) 位之后, \(first\) 表示此时两个串上的位置, \(second\) 为过了几个 \(a\) ,递推数组即可。
二分匹配长度,求得答案。
时间复杂度: \(O(len_a\cdot len_b\log(len_a\cdot b))\)
代码2
#include<bits/stdc++.h>
#define ll long long
#define PII pair<int,ll>
using namespace std;
const int N=110,INF=0x3f3f3f3f;
char a[N],c[N];
ll lena,lenc,b,d;
PII go[30][N*N];
inline int idx(int i,int j){
return i*lenc+j;
}
ll solve(int dis){
int tmp=idx(0,0);
ll res=0;
for(int i=0;i<30;i++){
if(dis>>i&1){
res+=go[i][tmp].second;
tmp=go[i][tmp].first;
}
}
return res;
}
int main(){
scanf("%lld%lld%s%s",&b,&d,a,c);
lena=strlen(a);lenc=strlen(c);
for(int i=0;i<lena;i++)
for(int j=0;j<lenc;j++){
int id=idx(i,j);
go[0][id]=make_pair(id,INF);
for(int k=0;k<lena;k++)
if(a[(i+k)%lena]==c[j]){
go[0][id]=make_pair(idx((i+k+1)%lena,(j+1)%lenc),(i==0)||((i+k)/lena));
break;
}
}
for(int i=1;i<30;i++)
for(int j=0;j<lena*lenc;j++){
int nxt=go[i-1][j].first;
go[i][j]=make_pair(go[i-1][nxt].first,go[i-1][j].second+go[i-1][nxt].second);
}
ll l=0,r=INF;
while(l<r){
ll mid=(l+r+1)>>1;
if(solve(mid)<=b) l=mid;
else r=mid-1;
}
printf("%lld\n",l/(lenc*d));
return 0;
}