题目大意:
给一个只含小写字母的长度为
n
n
n的字符串,问你最小修改量使得这个字符串中不存在任意一个长度
≥
2
\geq2
≥2的回文子串
n
≤
1
e
6
n\leq 1e6
n≤1e6
题目思路:
不存在长度等于2和3的回文子串即可。
那么也就是任意三个相邻的数,都满足两两不等。
显然有一个朴素的 d p ( i , j , k ) dp(i,j,k) dp(i,j,k)记录最后两位的情况。复杂度为 O ( n ∗ 2 6 3 ) O(n*26^3) O(n∗263)
考虑优化:
由于每个数都只与前两个和后两个数有关。那么根据鸽巢定理我们发现每个数只需要修改最多
2
2
2步即可。即最多五种修改状态(0,+1,+2,-1,-2)
例如最差情况(中间的c需要+=2到e):
abccda
所以dp只需要记录偏移量,复杂度降为: O ( n ∗ 5 3 ) O(n*5^3) O(n∗53).
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vll vector<ll>
#define fi first
#define se second
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
int a[maxn];
int dp[maxn][5][5];
int off (int y){
if (y == 0) return 0;
else if (y == 1) return 1;
else if (y == 2) return 2;
else if (y == 3) return -1;
return -2;
}
int trans (char x , int y){
if (x == '#') return -1;
int v = x - 'a';
v = (v + off(y) + 26)%26;
return v;
}
int main()
{
ios::sync_with_stdio(false);
int n; cin >> n;
string a; cin >> a;
a = '#' + a;
memset(dp , -1 , sizeof dp);
for (int i = 0 ; i < 5 ; i++) dp[1][0][i] = abs(off(i));
for (int i = 2 ; i <= n ; i++){
for (int j = 0 ; j < 5 ; j++){
for (int k = 0 ; k < 5 ; k++){
for (int l = 0 ; l < 5 ; l++){
if (dp[i - 1][l][k] == -1) continue;
int x = trans(a[i - 2] , l);
int y = trans(a[i - 1] , k);
int z = trans(a[i] , j);
int w = abs(off(j));
if (x == y || x == z || y == z) continue;
if (dp[i][k][j] == -1 || dp[i][k][j] > dp[i - 1][l][k] + w)
dp[i][k][j] = dp[i - 1][l][k] + w;
}
}
}
}
int ans = 1e9;
for (int i = 0 ; i < 5 ; i++){
for (int j = 0 ; j < 5 ; j++){
if (dp[n][i][j] == -1) continue;
ans = min(ans , dp[n][i][j]);
}
}
cout << ans << endl;
return 0;
}