最小(大)表示法是字符串问题中不同于匹配与失配的另一种O(n)的算法,它主要解决的是字符串的同构问题。将单个字符串循环左移右移算作该串的同构,最小(大)表示法能够在O(n)时间内求出这个串的所有同构串中的字典序最小的串的起始位置。由于代码简洁容易理解,因此在处理同类问题时往往比后缀数组以及各种匹配算法更加方便运用。
代码如下(注意下标是从0开始的,返回的下标也从0开始):
1 #include<stdio.h>
2 #include<string.h>
3 #include<algorithm>
4 using namespace std;
5
6 const int maxn=1e5+5;
7 char s[maxn<<1];
8
9 inline int max(int a,int b){return a>b?a:b;}
10 inline int min(int a,int b){return a<b?a:b;}
11
12 int MINR(char s[],int l){ //s是原串(未加倍过),l是原串长
13 for(int i=0;i<l;++i)s[l+i]=s[i]; //将s串加倍
14 s[2*l]=0;
15 int i=0,j=1; //利用i,j指针移动
16 while(i<l&&j<l){
17 int k=0;
18 while(s[i+k]==s[j+k]&&k<l)++k; //不断比较直到比较完长度为l的串或两个子串不相等
19 if(k==l)return min(i,j); //若比较出长度为l则直接返回靠前的那个串的开始位置
20 if(s[i+k]>s[j+k])i=max(i+k+1,j+1); //i串比j串大,那么i到i+k中的串都比j串大,i可以直接移动到i+k+1位置,而起始位置比j小的肯定都在j移动过程中比较过,所以i可以直接移动到j+1位置,因此取这两值的最大值
21 else j=max(j+k+1,i+1); //同上
22 }
23 return min(i,j); //返回位置靠前的下标
24 }
25
26 int MAXR(char s[],int l){
27 for(int i=0;i<l;++i)s[l+i]=s[i];
28 s[2*l]=0;
29 int i=0,j=1;
30 while(i<l&&j<l){
31 int k=0;
32 while(s[i+k]==s[j+k]&&k<l)++k;
33 if(k==l)return min(i,j);
34 if(s[i+k]<s[j+k])i=max(i+k+1,j+1);
35 else j=max(j+k+1,i+1);
36 }
37 return min(i,j);
38 }