字符串 最小表示法 O(n)算法 【模板】

来源:互联网 发布:电子邮件软件哪个好 编辑:程序博客网 时间:2024/05/29 15:12

求字符串的循环最小表示:

 

上面说的两个字符串同构的,并没有直接先求出Min(s),而是通过指针移动,当某次匹配串长时,那个位置就是Min(s)。而这里的问题就是:不是给定两个串,而是给出一个串,求它的Min(s),eg:Min(“babba”) = 4。那么由于这里并非要求两个串的同构,而是直接求它的最小表示,由于源串和目标串相同,所以处理起来既容易又需要有一些变化:我们仍然设置两个指针,i, j,其中i指向0,j指向1,仍然采用上面的滑动方式:

 

(1)  利用两个指针i, j。初始化时i指向0, j指向1。

 

(2)  k = 0开始,检验s[i+k] 与 s[j+k] 对应的字符是否相等,如果相等则k++,一直下去,直到找到第一个不同,(若k试了一个字符串的长度也没找到不同,则那个位置就是最小表示位置,算法终止并返回)。则该过程中,s[i+k] 与 s[j+k]的大小关系,有三种情况:

  证明的时候假设(i<j)的,无伤大雅 ;

     (A). s[i+k] > s[j+k],则i滑动到i+k+1处 --- 即s1[i->i+k]不会是该循环字符串的“最小表示”的前缀。

 证明如下

    

     (B). s[i+k] < s[j+k],则j滑动到j+k+1处,原因同上。

 证明如下


     (C). s[i+k] = s[j+k],则 k++; if (k == len) 返回结果。 

    注:这里滑动方式有个小细节,若滑动后i == j,将正在变化的那个指针再+1。直到p1、p2把整个字符串都检验完毕,返回两者中小于 len 的值。

 

(3)   如果 k == len, 则返回i与j中的最小值

 

      如果 i >= len   则返回j

 

      如果 j >= len   则返回i

如果看了上一篇文章 很容易对这里的i,j 产生误会  误以为i为ans,j为比较指针

实际上这题中 i,j 都可能存有ans 两者互相更新,直到有一个更新后超过了len(包括len) 的时候 另一个即为正解

(4)   进一步的优化,例如:i要移到i+k+1时,如果i+k+1 <= p2的话,可以直接把i移到 j+1,因为,j到j+k已经检验过了该前缀比以i到i+k之间任何一个位前缀都小;j时的类似,移动到i+1。



 下面来看一道题:
ZOJ 1729   Hidden Password 
Some time the programmers have very strange ways to hide their passwords. See for example how Billy "Hacker" Geits hide his password. Billy chooses a string S composed of small Latin letters with length L. Then he makes all L-1 one-letter left cyclic shifts of the string and takes as a password one prefix of the lexicographically first of the obtained strings (including S). For example let consider the string alabala. The cyclic one-letter left shifts (including the initial string) are:
alabala
labalaa
abalaal
balaala
alaalab
laalaba
aalabal
and lexicographically first of them is the string aalabal. The first letter of this string is in position 6 in the initial string (the positions in the string are counted from 0).
Write a program that for given string S finds the start position of the smallest lexicographically one-letter left cyclic shift of this string. If the smallest lexicographically left shift appears more than once then the program have to output the smallest initial position.
Your program has to be ready to solve more than one test case. The first line of the input file will contains only the number T of the test cases. Each of the following T lines will describe one test case - first the length L of the string (5 <= L <= 100000) and then, separated by one space, the string S itself.
The output file have to contain exactly T lines with a single number each - the initial position found by your program.


Sample Input
2
6 baabaa
7 alabala


Sample Output
1
6

 分析: 就是找一个字符串的最小表示,输出起始下标值。


 【AC代码】

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int N=1e6+10;int m;int next_t[N];char s1[N*2];int get_index(){    int i=0,j=1,k;    copy(s1,s1+m+1,s1+m); //将两个s1数组拼接成一个    //puts(s1);  //测试代码    for(k=0;k<m&&i<m&&j<m;) //找最小表示下标    {        if(s1[i+k]==s1[j+k]) k++; //相同就指针后移        else        {            if(s1[i+k]>s1[j+k]) i=i+k+1;            else j=j+k+1;            if(i==j)            {                j++;            }            k=0;        }    }    return min(i,j);}int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%d%s",&m,s1);        //puts(s1);        printf("%d\n",get_index());    }    return 0;}



原创粉丝点击