UOJ Logo kczno1的博客

博客

hackerrank Suffix Rotation

2017-12-25 18:04:14 By kczno1

https://www.hackerrank.com/contests/w28/challenges/suffix-rotation/problem

首先最后一定是能排成从小到大的,且每次一定是把最小的字符往前移。

而且无论如何移动前i个字符,第i+1之后的字符在环上的相对位置一定是不变的。

那么当第i个字符最后一位确定了之后,后面的字符串一定是固定的。

用$dp[j]$表示排完前$i(i=s[j])$个字符,最后一位为$j$的最小步数。

考虑从第$i-1$个字符到第$i$个字符的过程,每一块连续的$i$都是一起移动的,而且每次都是把一个块的开头移动到前面。

所以说$dp[j]$的意思更正为最后一块为$j$。(可以让每个块用开头代表)

考虑每个第$i-1$个字符的最后一块$k$的贡献,

定义$next[k]$为删去前$i-1$个字符后块$k$后面的第一个位置,$cnt[i]$为字符$i$的块的个数。

若$s[next[k]]!=i$,则对任意$j,dp[j]=dp[k]+cnt[i]$

若$s[next[k]]=i$,

设$next[k]$所属的块为$j'$

(1)若$next[k]$不是$j'$的块首,那么同上

(2)若$next[k]$是$j'$块首

对$j!=j'$,因为块$j'$不需要移动,所以$dp[j]=dp[k]+cnt[i]-1$

对$j'$,若$cnt[i]>1$,则其他$j$必须先移动,所以$j'$仍需要移动,所以$dp[j']=dp[k]+cnt[i]$;

          若$cnt[i]=1$,则$dp[j']=dp[k]$

记个最小值就$O(n)$了

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define pb push_back
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,r,l) for(int i=r;i>=l;--i)

template <typename T> inline void chmin(T &x,const T &y)
{
    if(x>y)x=y;
}
template <typename T> inline void chmax(T &x,const T &y)
{
    if(x<y)x=y;
}

const int N=1e3+5,U=256;
int n;
char s[N];
int pre[N],nex[N];
int a[U][N];
int dp[N];

int main()
{
    freopen("1.in","r",stdin);freopen("1.out","w",stdout);
    int tt;
    cin>>tt;
    while(tt--)
    {
        scanf("%s",s+1);
        int n=strlen(s+1);
        char mx=0;
        rep(i,1,n)chmax(mx,s[i]);

        rep(i,1,n) {nex[i]=i+1;pre[i]=i-1;}
        pre[1]=n;nex[n]=1;

        rep(i,0,mx)*a[i]=0;
        rep(i,1,n)a[s[i]][++*a[s[i]]]=i;
        rep(c,0,mx-1)
        {
            int top=0;
            rep(i,1,*a[c])
            {
                int x=a[c][i];
                if(s[pre[x]]==c) 
                {
                    nex[pre[x]]=nex[x];
                    pre[nex[x]]=pre[x];
                }
                else a[c][++top]=a[c][i];
            }
            *a[c]=top;
            rep(i,1,top) 
            {
                int x=a[c][i];
                nex[pre[x]]=nex[x];
                pre[nex[x]]=pre[x];
            }
        }

        int lastc=0;
        *a[0]=1;
        a[0][1]=0;
        nex[0]=1;
        dp[0]=0;
        rep(c,1,mx-1)
        if(*a[c])
        {
            int mn=N,cant=-1;
            rep(i,1,*a[lastc])
            {
                int k=a[lastc][i];
                int j=nex[k];
                if(s[j]==c&&s[pre[j]]!=c) 
                {
                    int now=dp[k]-1;
                    if(now<mn)
                    {
                        mn=now;
                        if(*a[c]==1) cant=-1;
                        else cant=j;
                    }
                    else 
                    if(now==mn)cant=-1;
                }
                else
                {
                    int now=dp[k];
                    if(now<mn){mn=now;cant=-1;}
                    else 
                    if(now==mn)cant=-1;
                }
            }
            mn+=*a[c];
            rep(i,1,*a[c])
            {
                int j=a[c][i];
                if(j!=cant)dp[j]=mn;
                else dp[j]=mn+1;
            }
            lastc=c;
        }

        int ans=N;
        rep(i,1,*a[lastc])chmin(ans,dp[a[lastc][i]]);
        printf("%d\n",ans);
    }
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。