字符串交换相邻位置形成回文串
题意:给出一个字符串, 字符串的长度大概在10^6左右, 现在只能交换相邻两个的字符,现在要求出最少需要多少步能够形成一个回文串。
思路: 这道题开始的时候猜了一种做法, AC之后才想通这样的正确性。
从左向右考虑, 最左边的是u,那么找到最右边的u,然后把最右边的移动到最外边,这样子,最外边一样了,然后在考虑里边的,直至最后。最后把步数加起来就行了。 如果剩下一个单的,最后移动到中间就行了。
其实移动到最外边只是这个位置不再有东西了, 于是我们可以用树状数组统计步数。
下面说一下这样做的正确性。 对于最外的u的位置为i,最右边u的位置j,那么想要对应需要的最小步数是一定。如果把他们放到中间只会影响别人,放在外边也是最小的, 但是不影响别人。 这道题首先判断奇数的字母是否多余1, 如果多余1,直接输出-1,否则输出答案。
AC代码:
View Code
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <queue> #include <algorithm> #include <vector> using namespace std; typedef long long LL; const int N = 1100005, INF = 1<<25; int in[N], n, cc[129]; bool used[N]; char str[N]; vector<int>g[129]; int lowbit(int t) { return t&(t^(t-1)); } int sum(int end) { int ans=0; while(end>0) { ans+=in[end]; end-=lowbit(end); } return ans; } void add(int pos ,int num) { while(pos<=n) { in[pos]+=num; pos+=lowbit(pos); } } void solve() { memset(in, 0, sizeof(in)); for(int i=1; i<=n+1; i++) { add(i,1); } LL ans = 0; int v, u, size; char s; memset(used, 0, sizeof(used)); for(int i=1; i<=n; i++) { if(!used[i]) { s = str[i]; size = g[s].size(); u = g[s][size-1]; if(i == u) { ans += (sum(n) - sum(i))/2; } else { ans += sum(n) - sum(u); } add(u, -1); add(i, -1); used[i] = used[u] = 1; g[s].erase(g[s].end()-1); } } printf("%lld", ans); puts(""); } int main() { while(scanf("%s", str+1) != EOF) { n = strlen(str+1); memset(cc, 0, sizeof(cc)); for(int i=1; i<129; i++) g[i].clear(); for(int i=1; i<=n; i++) { cc[str[i]]++; g[str[i]].push_back(i); } int flag = 0; for(int i=0; i<26; i++) { if(cc['a'+i]%2) flag++; if(cc['A'+i]%2) flag++; } if(flag > 1) { printf("-1"); puts(""); continue; } else { solve(); } } return 0; }


浙公网安备 33010602011771号