统计重复计数 题解
题目跳转:P10976 统计重复个数
题目大意
给定 \(s1\),\(n1\),\(s2\),\(n2\) 定义 \([str,n]\) 表示 \(n\) 个 \(str\) 头尾相接,求最大的 \(m\) 满足 \([s2,n2\times m]\) 是 \([s1,n1]\) 的子序列。
思路分析
这是一道倍增优化 \(dp\) 的好题。让我们一步一步来,从最简单的开始。
首先发现 \(str=[str_2,m]=[[s2,n2],m]=[s2,m\times n2]\)。
那么 \(str\) 就是 \(s2\) 复制了 \(m\times n2\) 遍。
设 \(dp_{i,j}\) 表示从 \(s1_i\) 开始,往后多少个字符能生成 \([s2,j]\)
那么转移也就出来了(下面不理解的先在纸上画个图,对着上面的状态设计多看几遍就懂了):
其中:
原始状态中 \(j\) 最大需要达到 \(m\times n2\),但 \(m\) 是待求答案,不能直接作为数组范围使用。题目约束为:
答案 \(m\) 可能达到 \(10^8\) 量级。这样肯定是不行的,不仅会 \(TLE\) 还开不下 \(dp\) 数组。
我们再次观察状态转移:
发现里面有许多无用的状态,这时候就可以用倍增来优化。
再次设 \(dp_{i,j}\) 表示从 \(s1_i\) 开始,往后多少个字符能匹配出 \([s2,2^j]\) 作为子序列。
先来说说原理:
给你任意一个正整数 \(x\),都可以用若干个 \(2\) 的次方数相加得到。例如:
解释起来也非常简单,就是把 \(x\) 写成二进制,\(2\) 的次方数正好对应 \(x\) 的二进制上为 \(1\) 的位置。
那么回到这个题,状态设计好了,转移也就简单了:
求答案的时候从大到小枚举倍增指数 \(k\),设当前已经消耗的字符数为 \(pos\),初始 \(pos=0\)。如果
也就是还在 \(str_1\) 的范围内,那就把 \(2^k\) 计入已经匹配的 \(s2\) 个数,同时更新:
最终的答案要再除以 \(n2\)。
为什么要从大到小枚举 \(j\) 呢?看下面这个例子:
从大到小:
从小到大:
枚举顺序不同造成了答案错误。
现在还要再考虑 \(dp_{i,0}\) 怎么求,观察到 \(1\leq\lvert s1\rvert,\lvert s2\rvert\leq 100\),所以直接暴力枚举就行(具体看代码),这里要注意内层枚举位置 \(i\) 的范围是:\(i\lt min(\lvert s1\rvert\times n1,p+\lvert s1\rvert\times \lvert s2\rvert)\),不然会超时。
时间复杂度:\(O(\lvert s1\rvert^2\times\lvert s2\rvert+\lvert s1\rvert\times log\space m)\)
注意:
- 多测要清空。
- 要注意 \(dp\) 数组的第一维在枚举时会超出 \(\lvert s1\rvert\),所以记得取模。
- 因为 \(\lvert s2\rvert\times n2\leq 10^8\),所以倍增指数最大约到 \(log_210^8\approx 27\),\(dp\) 数组第二维应至少开到 \(28\)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s1,s2;
ll n1,n2,dp[105][28],ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
while(cin>>s2>>n2>>s1>>n1){
ll n=s1.size(),m=s2.size();
for(ll i=0;i<n;i++){
for(ll j=0;j<=27;j++){
dp[i][j]=1e18;
}
}
for(ll p=0;p<n;p++){
for(ll i=p,j=0;i<min(n*n1,p+m*n)&&j<m;i++){
if(s1[i%n]==s2[j]){
j++;
}
if(j==m){
dp[p][0]=i-p+1;
break;
}
}
}
for(ll j=1;j<=27;j++){
for(ll i=0;i<n;i++){
dp[i][j]=min(dp[i][j],dp[i][j-1]+dp[(i+dp[i][j-1])%n][j-1]);
}
}
ans=0;
for(ll i=27,j=0;i>=0;i--){
if(j+dp[j%n][i]<=n*n1){
j+=dp[j%n][i];
ans+=(1<<i);
}
}
cout<<ans/n2<<"\n";
}
return 0;
}

浙公网安备 33010602011771号