2022.10.23 总结
1. 逐月 P5083
题意
有一个字符串 \(S\)。
可以做很多次操作,每次操作将 \(S\) 的第一个字符删去或是将最后一个字符删去,再把原来的字符串加在前面或后面。
现在给定很多次操作后的字符串,请你求出有多少种方案能让初始串变成目标串。
思路
100 分
因为字符串的每次操作都是将原串的第一个字符删去或是将最后一个字符删去,再把原串加在前面或后面,所以,可以通过目标字符串推出原串,每次将字符串分成两半即可,分治。
由于每次都是将长度 $ \times 2 - 1$,所以在不是初始字符串时,这个字符串的长度一定是奇数。
但是这里又有 \(4\) 种方法。
-
ABC -> ABCBC
-
ABC -> ABCAC
-
ABC -> ABABC
-
ABC -> BCABC
所以,每次需要做 \(4\) 次还原。
如果是 ABCDE 这样的字符串,虽然也是奇数个字符,但是并不满足题目要求,所以需要判断字符串的两个子串是否相等。
时间复杂度
每次都是将字符串的长度变成原来的一半,\(O(\log n)\)。
每次最多有四种还原的方法,\(O(4)\)。
求子串是否相等,\(O(n)\)
总时间复杂度为 \(O(4 ^ {\log n} \times n)\),也就是 \(O(n ^ 3)\)。
空间复杂度
字符串记录目标字符串,\(O(n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
string s;
int cnt;
string sub(int l, int len){ // 寻找子串
string t;
for (int i = l; i < l + len; i++) {
t += s[i];
}
return t;
}
void dfs(int l, int r){
cnt++;
if (r - l + 1 < 2 || (r - l + 1) % 2 == 0) {
return ;
}
int n = (r - l) / 2 + 1;
// 2 ~ n , 1 ~ n
if (sub(l, n - 1) == sub(l + n, n - 1)) {
dfs(l + n - 1, r);
}
// 1 ~ n - 1 , 1 ~ n
if (sub(l, n - 1) == sub(l + n - 1, n - 1)) {
dfs(l + n - 1, r);
}
// 1 ~ n , 1 ~ n - 1
if (sub(l, n - 1) == sub(l + n, n - 1)) {
dfs(l, l + n - 1);
}
// 1 ~ n , 2 ~ n
if (sub(l + 1, n - 1) == sub(l + n, n - 1)) {
dfs(l, l + n - 1);
}
}
int main(){
freopen("scode.in", "r", stdin);
freopen("scode.out", "w", stdout);
cin >> s;
dfs(0, s.size() - 1);
cout << cnt - 1;
return 0;
}