题意: 两个操作:在一个串前面或后面添加一个字母。把当前这个串double一下变成回文。 询问一个串的最小操作步数,开始为空。 题解: 答案肯定是一个回文子串的最小步数加上剩下的一个个添。 一个回文串s可以从两边加同一个字母一步花费转移过来,或者从长度小于|s|/2的回文后缀转移过来。 回文树上dp,维护长度小于一半的后缀link #include #include #include #include #include #include #include #include #include #include using namespace std; #define N 200010 #define M 2000010 #define ULL unsigned long long #define LL long long #define ls (i << 1) #define rs (ls | 1) #define md ((ll + rr) >> 1) #define lson ll, md, ls #define rson md + 1, rr, rs #define inf 0x3f3f3f3f #define eps 1e-4 #define pii pair #define MP make_pair #define mod 1000000007 int n; int ans; char s[N]; struct PT { int S[N], n, len[N], last, fail[N], h[N]; int dp[N]; int nxt[N][4], tot; int newnode(int l) { int k = tot++; len[k] = l; memset(nxt[k], 0, sizeof nxt[k]); return k; } void init() { tot = 0; newnode(0); newnode(-1); S[n=0] = -1; last = fail[1] = fail[0] = 1; h[0] = h[1] = 1; dp[0] = 1; } int get(int x) { while(S[n-len[x]-1] != S[n]) x = fail[x]; return x; } void add(int c) { S[++n] = c; int cur = get(last); if(nxt[cur][c] == 0) { int now = newnode(len[cur] + 2); fail[now] = nxt[get(fail[cur])][c]; nxt[cur][c] = now; if(len[now] % 2 == 0) { h[now] = h[cur]; for(int &i = h[now]; S[n-len[i]-1] != S[n] || len[i] + 2 > len[now]/2;) { i = fail[i]; while(len[i] >= 0 && len[i] & 1) i = fail[i]; } h[now] = len[h[now]] >= 0 ? nxt[h[now]][c] : 0; dp[now] = min(dp[cur] + 1, len[now]/2 - len[h[now]] + dp[h[now]] + 1); ans = min(ans, ::n - len[now] + dp[now]); //printf("n %d len[%d]:%d dp %d h %d ans %d\n", n, now, len[now], dp[now], len[h[now]], ans); } } last = nxt[cur][c]; } }go; int get(char c) { if(c == 'A') return 0; else if(c == 'T') return 1; else if(c == 'C') return 2; return 3; } int main() { int T; scanf("%d", &T); while(T--) { scanf("%s",s); n = strlen(s); ans = n; go.init(); for(int i = 0; i < n; ++i) go.add(get(s[i])); printf("%d\n", ans); } }