《算法进阶指南》——倍增DP-计算重复
用f[i][j]表示从s1i点开始组成2^j个s2最短要多长,由于要求"最短" 所以一定以s2的最后一个字符结尾。这样f[i][j] 与f[i+f[i][j]] 拥有可合并性,可以用倍增DP。先预处理f[i][0],状态转移方程为
f[i][j] = f[i][j-1] + f[i+f[i][j-1]][j-1];
#include <bits/stdc++.h>
#define int long long
#define INF 1e18
#define endl '\n'
#define MOD (int)(998244353)
using namespace std;
string s1, s2;
int n1, n2;
int f[102][31];
bool check()
{
int len1 = s1.size(), len2 = s2.size();
vector<int> letters(26, 0);
for (int i = 0; i < len1; i++)
{
letters[s1[i] - 'a']++;
}
int ok = 1;
for (int i = 0; i < len2; i++)
{
if (letters[s2[i] - 'a'] == 0)
{
ok = 0;
break;
}
}
return ok;
}
void init_f0()
{
int len1 = s1.size(), len2 = s2.size();
for (int i = 0; i < len1; i++)
{
int s2index = 0;
for (int j = 0;; j++)
{
int s1index = (i + j) % len1;
if (s2[s2index] == s1[s1index])
{
s2index++;
}
if (s2index == len2)
{
f[i][0] = j + 1;
break;
}
}
}
}
void solve()
{
while (cin >> s2 >> n2 >> s1 >> n1)
{
memset(f, 0, sizeof(f));
int len1 = s1.size(), len2 = s2.size();
if (check() == 0)
{
cout << 0 << endl;
continue;
}
init_f0();
for (int j = 1; j < 30; j++)
{
for (int i = 0; i < len1; i++)
{
int nows1index = i, nexts1index = (i + f[i][j - 1]) % len1;
f[i][j] = f[i][j - 1] + f[nexts1index][j - 1];
}
}
int sumlen = 0;
int s1index = 0;
int maxs2 = 0;
for (int i = 29; i >= 0; i--)
{
if (sumlen + f[s1index][i] <= n1 * len1)
{
maxs2 += pow(2, i);
sumlen = sumlen + f[s1index][i];
s1index = (s1index + f[s1index][i]) % len1;
}
}
cout << maxs2 / n2 << endl;
}
}
signed main()
{
// std::ios::sync_with_stdio(0);
// std::cin.tie(0);
int T = 1;
// cin >> T;
for (int i = 1; i <= T; i++)
{
solve();
}
return 0;
}
收获:
倍增DP两个步骤:1.预处理出2^i长度的的结果 2.按二进制拼凑出问题要求的区间
要求:选择的状态要有区间可加性,能够直接处理出长度为1的情况

浙公网安备 33010602011771号