dp字符串匹配
Text Editor
链接:https://codeforces.com/contest/1701/problem/E
题意:
你想写一个由字母组成的文本 T。但是,您已经编写了一个长度为N的小写字符串S,现在您希望通过从S中获取T来修复它。
最初,文本编辑器的光标位于文本 S的末尾(在最后一个字符之后)。在一个步骤中,您可以执行下列操作之一:
按下“左”按钮,光标就会向左移动一个位置(如果光标指向文本的开头,也就是第一个字符之前,光标就什么也不做) ;
按下“右”按钮,光标就会向右移动一个位置(如果光标指向文本的末尾,也就是在最后一个字符之后,光标就什么也不会做) ;
按下“主页”按钮,将光标移动到文本的开头(文本的第一个字符之前) ;
按“结束”按钮,将光标移动到文本的末尾(在文本的最后一个字符之后) ;
按下“退格”按钮,这样光标之前的字符将从文本中删除(如果光标之前没有字符,则不会发生任何操作)。
求最小操作数,如果无法获取就输出 -1。
const int N = 5e3 + 10, M = 1e6 + 5, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
const double eps = 1e-8; //一般比小数点的位数至少多 2
const double pi = 3.141592653589;
const int P = 131;
int sum1[N]; // 前缀中b的第i个字母和a的第sum1[i]匹配
int dp1[N][N];
int dp2[N][N];
void solve()
{
int n, m;
cin >> n >> m;
string a, b;
cin >> a >> b;
a = " " + a;
b = " " + b;
for (int i = 1, j = 1; i <= m; ++i, ++j)
{
while (j <= n && a[j] != b[i])
j++;
sum1[i] = j;
}
sum1[m + 1] = n + 1;
if (sum1[m] > n) // 不可能匹配上
{
cout << "-1" << endl;
return ;
}
// 初始化
for (int i = 0; i <= n + 1; ++i)
for (int j = 0; j <= m + 1; ++j)
dp1[i][j] = dp2[i][j] = 0;
// 每一个长度为i的字符串变成长度为j的匹配字符串不需要移动的花费
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (a[i] == b[j])
dp1[i][j] = dp1[i - 1][j - 1] + 1;
for (int i = n; i >= 1; --i)
for (int j = m; j >= 1; --j)
if (a[i] == b[j])
dp2[i][j] = dp2[i + 1][j + 1] + 1;
int ans = 2 * n;
for (int fjd = 0; fjd <= m; ++fjd) // 枚举T字符串的前后分界点
for (int i = sum1[fjd]; i <= sum1[fjd + 1] - 1; ++i)
{
// 向右移动需要i步,i-fjd为要删除的字母数
// 如果可以通过前面状态继承,则不需要删除,所以要减去dp1[i][fjd]
int spend1 = i - fjd + i - dp1[i][fjd];
// 向左移动需要n-i步,同时删除字母操作相当于向前移动了,所以不需要增加操作数目
// 继承,减去dp2[i+1][fjd+1]
int spend2 = n - i - dp2[i + 1][fjd + 1];
ans = min(ans, spend1 + spend2 + (spend1 > 0)); // 只有大于0时需要跳转
}
cout << ans << endl;
return ;
}
signed main()
{
IOS;
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
组合数
D. Madoka and The Corruption Scheme
链接:https://codeforces.com/contest/1717/problem/D
题意:有2^n个人,两两之间按照位置顺序比赛(这个位置顺序是可以自己随意排的),也可以自己决定每场是左边还是右边赢,决定完之后,赞助商有k次修改机会(如果原来决定左边赢,修改后则变成右边赢)。
思路:每场比赛都决定左边赢,如果赞助商要修改,唯有修改黑色边<=k的才能让该数字编号赢。
假如赞助商可以修改k次,那么列举,修改0次能赢的有C(n, 0),修改1次能赢的有C(n, 1),……,修改k次能赢的有C(n, k)。
由二项式可得C(n, 0) + C(n, 1) + …… + C(n, n) = 2^n。
将树和二进制对应,就是一个n位二进制,0~2^(n-1)和每个路径一一对应,黑边对应其中的1,在这些数中,包含恰好i个1的有C(n, i)个。
const int N = 2e5 + 10, M = 2e6 + 5, INF = 0x3f3f3f3f;
const double eps = 1e-8; //一般比小数点的位数至少多 2
const double pi = 3.141592653589;
const LL MOD = 1e9 + 7;
int fact[N + 10], infact[N + 10];
void F(int n, int p)
{
fact[0] = infact[0] = 1;
for (int i = 1; i <= n; ++i)
{
fact[i] = (LL)fact[i - 1] * i % p;
infact[i] = (LL)infact[i - 1] * fastpow(i, p - 2, p) % p;
}
}
int C(int a, int b, int p)
{
if (a < b)
return 0;
return (LL)fact[a] * infact[a - b] % p * infact[b] % p;
}
void solve()
{
LL n, k;
cin >> n >> k;
F(n, MOD);
if (k >= n)
cout << fastpow(2, n, MOD) << endl;
else
{
LL ans = 0;
for (int i = 0; i <= k; ++i)
ans = (ans + C(n, i, MOD)) % MOD;
cout << ans << endl;
}
}
signed main()
{
IOS;
/*int T;
cin >> T;
while (T--)*/
solve();
Waku waku;
}