A1043. 完美的代價
時間限制:
1.0s 內存限制:
512.0MB
問題描述
迴文串,是一種特殊的字符串,它從左往右讀和從右往左讀是同樣的。小龍龍認爲迴文串纔是完美的。如今給你一個串,它不必定是迴文的,請你計算最少的交換次數使得該串變成一個完美的迴文串。
交換的定義是:交換兩個相鄰的字符
例如mamad
第一次交換 ad : mamda
第二次交換 md : madma
第三次交換 ma : madam (迴文!完美!)
交換的定義是:交換兩個相鄰的字符
例如mamad
第一次交換 ad : mamda
第二次交換 md : madma
第三次交換 ma : madam (迴文!完美!)
輸入格式
第一行是一個整數N,表示接下來的字符串的長度(N <= 8000)
第二行是一個字符串,長度爲N.只包含小寫字母
第二行是一個字符串,長度爲N.只包含小寫字母
輸出格式
若是可能,輸出最少的交換次數。
不然輸出Impossible
不然輸出Impossible
樣例輸入
5
mamad
mamad
樣例輸出
3
【題解】【字符串處理+歸併排序求逆序對】
【這道題的作法是求出將整個串反轉須要的最小步數,而後由於是求到迴文串的步數,因此除以二便可】
#include<cstdio>
#include<cstring> #include<iostream> #include<algorithm> using namespace std; char s[8010]; int d[8010],n,nxt[8010],pre[8010],a[8010],a1[8010],ans=0; bool p[8010],t=true; inline void js(int f,int m,int l) { int i=f,j=m+1,k=f; while (i<=m&&j<=l) if(a[i]>a[j]) a1[k++]=a[j++],ans+=(m-i+1); else a1[k++]=a[i++]; while(i<=m) a1[k++]=a[i++]; while(j<=l) a1[k++]=a[j++]; for(i=f;i<=l;++i) a[i]=a1[i]; return; } inline void gb(int f,int l) { if(f<l) { int m=(f+l)/2; gb(f,m); gb(m+1,l); js(f,m,l); } return; }//歸併排序求逆序對 int main() { int i; scanf("%d",&n); for(i=0;i<n;++i) cin>>s[i],d[s[i]-'a']++;//讀入並記錄每一個字符出現了多少次 for(i=0;i<=25;++i) if(d[i]&1==1) if(!t) {printf("Impossible"); return 0;} else t=false;//由於是迴文字串,因此最多隻能出現一個出現次數爲奇數的字符 memset(d,-1,sizeof(d)); for(i=n-1;i>=0;--i) nxt[i]=d[s[i]-'a'],d[s[i]-'a']=i;//存每一個字符的出現位置(當前位置存下一次出現的位置,倒着循環) memset(d,-1,sizeof(d)); for(i=0;i<n;++i) pre[i]=d[s[i]-'a'],d[s[i]-'a']=i;//存每一個字符出現的位置(當前位置存上一次出現的位置,正着循環) memset(p,false,sizeof(p)); for(i=0;i<n;++i) if(!p[i]) { int u=i,v=d[s[i]-'a']; while(v!=-1) { a[n-u-1]=v; p[u]=true; u=nxt[u]; v=pre[v];//指針後移(u表明的是翻轉後的字串) } }//爲每一個位置的字符編號(按它徹底倒置後的樣子編),取最近的一個位置的當前字符(由於每次只能向左或向右移動一個單位,爲了保證步數儘量小) gb(0,n-1);//歸併排序求逆序對 printf("%d\n",ans/2);//由於是求到迴文的步數,因此至關於只變一半 return 0; }//從1開始存就錯,不知爲何……