【tsinsen A1043】完美的代價(字符串處理+歸併排序求逆序對)

 
A1043. 完美的代價
時間限制: 1.0s   內存限制: 512.0MB  
總提交次數: 1985   AC次數: 446   平均分: 44.86
問題描述
  迴文串,是一種特殊的字符串,它從左往右讀和從右往左讀是同樣的。小龍龍認爲迴文串纔是完美的。如今給你一個串,它不必定是迴文的,請你計算最少的交換次數使得該串變成一個完美的迴文串。
  交換的定義是:交換兩個相鄰的字符
  例如mamad
  第一次交換 ad : mamda
  第二次交換 md : madma
  第三次交換 ma : madam (迴文!完美!)
輸入格式
  第一行是一個整數N,表示接下來的字符串的長度(N <= 8000)
  第二行是一個字符串,長度爲N.只包含小寫字母
輸出格式
  若是可能,輸出最少的交換次數。
  不然輸出Impossible
樣例輸入
5
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開始存就錯,不知爲何……
相關文章
相關標籤/搜索