字符串的最小表示法[hdu5442]

来源:互联网 发布:网络工程师和程序员 编辑:程序博客网 时间:2024/05/15 02:06

题目描述

Lulu has a sweet tooth. Her favorite food is ring donut. Everyday she buys a ring donut from the same bakery. A ring donut is consists of n parts. Every part has its own sugariness that can be expressed by a letter from a to z (from low to high), and a ring donut can be expressed by a string whose i-th character represents the sugariness of the i−th part in clockwise order. Note that z is the sweetest, and two parts are equally sweet if they have the same sugariness.

Once Lulu eats a part of the donut, she must continue to eat its uneaten adjacent part until all parts are eaten. Therefore, she has to eat either clockwise or counter-clockwise after her first bite, and there are 2n ways to eat the ring donut of n parts. For example, Lulu has 6 ways to eat a ring donut abc: abc,bca,cab,acb,bac,cba. Lulu likes eating the sweetest part first, so she actually prefer the way of the greatest lexicographic order. If there are two or more lexicographic maxima, then she will prefer the way whose starting part has the minimum index in clockwise order. If two ways start at the same part, then she will prefer eating the donut in clockwise order. Please compute the way to eat the donut she likes most.

算法思路

  1. 这一题的题目还是十分明确的,就是求一个字符串的最小的循环子串。
  2. 看过OI集训队周源的一篇论文,我们要求的是这个字符串的一个最大的表示,可以使用下面的算法来完成
    3.算法的具体思路:
    (1)开始时,将字符串复制两份,设置两个指针,i=0,j=1.
    (2)k=0,然后反复迭代直到s[i+k]!=s[j+k]
    (3)如果k=n,那么返回较小的值,否则看情况滑动指针
    4.算法正确性的证明
    (1)算法返回的一定是字典序最大的那个循环子串
    首先我们设存在一个子串比返回的串更大,而由我们的算法的迭代,我们知道|ij|<n , 为了方便,我们设i<j,则我们知道存在一个位置t,使得str[i]<str[t],并且str[t]=max(str[it]) , 则我们知道j一定会滑动到这个位置。因为从i-t没有一个字符可以和str[t]匹配。
    (2)在找到循环节的时候,最小循环字符串的起始地址一定是nlen+min(i,j),len=abs(ij)
    证明:当我们在循环中找到一个循环节的时候,根据上面的性质,我们知道这个时候i,j都是指向了最大的那个字符,那么我们要做的就是根据这个循环节找到这个最大的字符最后出现的位置。很显然,我们知道这个位置一定会在[nlen,n1]当中,但是这个位置究竟在哪里呢?
    很显然,我们所要的值就在min(i,j)的位置,这个值也一定小于len,因为如果这个字符找到第一个循环节中的最大的字符的时候,就不会存在其他的字符能够使得这个指针再次滑动了。所以,我们的结论就可以得到证明了。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;#define MAXN 40005int n,t;char clockwise[MAXN],anticlockwise[MAXN];int processa(char* s){//cal the min index    int i=0,j=1,k;    while(i<n&&j<n){        k = 0;        while(s[i+k]==s[j+k]&&k<n)k++;        if(k==n)return min(i,j);        if(s[i+k]>s[j+k]){            if(j+k+1>i)                j = j+k+1;            else                j = i+1;        }        else{            if(i+k+1>j)                i = i+k+1;            else                i = j+1;        }    }    return min(i,j);}int processb(char* s){//calulate the max index    int i=0,j=1,k;    while(i<n&&j<n){        k = 0;        while(s[i+k]==s[j+k]&&k<n)k++;        if(k==n){            int len = abs(i-j);            return n-len+min(i,j);        }        if(s[i+k]>s[j+k]){            if(j+k+1>i)                j = j+k+1;            else                j = i+1;        }        else{            if(i+k+1>j)                i = i+k+1;            else                i = j+1;        }    }    if(j>=n)return i;    else return j;}int main(){    freopen("input","r",stdin);    int i;    scanf("%d",&t);    while(t--){        scanf("%d",&n);        scanf("%s",clockwise);        for(i=0;i<n;i++){            clockwise[n+i]=clockwise[i];        }        clockwise[2*n] = '\0';        for(i=0;i<2*n;i++)            anticlockwise[i] = clockwise[2*n-i-1];        int ans1 = processa(clockwise);        int ans2 = processb(anticlockwise);        //printf("%d %d\n",ans1,ans2);        int l = strncmp(clockwise+ans1,anticlockwise+ans2,n);        if(l==0){            if(n-ans2<ans1+1)                printf("%d 1\n",n-ans2);            else printf("%d 0\n",ans1+1);        }        else if(l>0)            printf("%d 0\n",ans1+1);        else            printf("%d 1\n",n-ans2);    }    return 0;}
0 0
原创粉丝点击