[SCU 4504] 奶牛合影 (最小表示法)

来源:互联网 发布:nginx 子域名跨域配置 编辑:程序博客网 时间:2024/05/29 21:34

SCU - 4504

给定一个循环串,问从哪个位置剖分能使得字典序最小


最小表示法裸题,后缀数组裸题
然而后缀数组我还不太会构造
所以转而学习了一下最小表示法

朴素算法:
将原数组复制一遍
枚举两个串的开头p1, p2,依次比较两个串的每一位
每当 S[p1+k]S[p2+k] 时,字典序较大的头指针向后移一位
时间复杂度 O(N2)

最小表示法:
与朴素算法大致相同
就是在发现 S[p1+k]S[p2+k]时,字典序较大的头指针向后移动 k位

证明如下:

  1. 不妨设 S[p1+k]>S[p2+k],则 i[p1,k) 都不可能成为最小串的头
    因为如果其为最小串的头,那么可以在第二个串中对应找一个位置
    使得 Si1,i1+k>Si2,i2+k
    所以 i[p1,k) 都不可能成为最小串的头

  2. 同理 S[p1+k]<S[p2+k]i[p2,k) 都不可能成为最小串的头
    所以在发生失配时,头指针直接向后移动k+1

  3. S[p1+k]=S[p2+k],则 k就自增,不断比较,直到失配
    或者当k==N时,此时就得到了最小串
    p1p2 不妨设 p2>p1
    由 1和 2可知,p1>p2的字符都不可能是最小串的开头
    由于 S1==S2,所以相同结论可以映射到 S2
    所以就可得出 p1即最小串的开头

最后 p1p2中较小的那个就是最小串的头指针
如果 p1p2都小于N,最小串不唯一,任意一个都行
如果最小串唯一,那么有一个势必滑动出 N了,所以取最小的
显然尾指针最多移动 4*N次,所以算法时间复杂度 O(N)

#pragma comment(linker, "/STACK:102400000,102400000")#include <cstdio>#include <iostream>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>#include <map>#include <set>#include <queue>using namespace std;typedef pair<int,int> Pii;typedef long long LL;typedef unsigned long long ULL;typedef double DBL;typedef long double LDBL;#define MST(a,b) memset(a,b,sizeof(a))#define CLR(a) MST(a,0)#define Sqr(a) (a*a)const int maxn=3e5+10;int N;int inpt[2*maxn];int main(){    int T;    scanf("%d", &T);    for(int ck=1; ck<=T; ck++)    {        scanf("%d", &N);        for(int i=0; i<N; i++)        {            scanf("%d", &inpt[i]);            inpt[i+N]=inpt[i];        }        int p1=0,p2=1;        while(p1<N&&p2<N)        {            int len=0;            while(inpt[p1+len]==inpt[p2+len]&&len<N) len++;            if(len==N) break;            if(inpt[p1+len]>inpt[p2+len]) p1+=len+1;            else p2+=len+1;            if(p1==p2) p2++;        }        int ansp=min(p1, p2);        for(int i=ansp; i<ansp+N; i++) printf("%d ", inpt[i]);        puts("");    }    return 0;}
0 0
原创粉丝点击