题意:
给出一个字符串,求其所有循环同构串中字典序最大的串以及最小的串。并且计算这两个串在所有循环同构串中出现的次数
解法:
第一问,用经典的求串的最小表示的算法就可以了。
第二问,利用KMP算法前缀数组的性质,即最大后缀等于最长前缀的位置。从最小表示(最大表示)的位置开始计算next函数,将start+len-1位置作合法标志,计算nxt数组的时候顺便计算标记(与AC自动机方法相同),然后统计标记个数即可~
代码:(GCC)
1 # include <string.h>
2 # include <stdio.h>
3 # include <stdbool.h>
4 char str[2050000];
5 int pre[2050000];
6 bool end[2050000];
7 int maxpos(int len)
8 {
9 int p1=0,p2=1,l=0,i;
10 while(p1<len&&p2<len&&l<len)
11 {
12 int cmp=str[p1+l]-str[p2+l];
13 if(!cmp)
14 l++;
15 else
16 {
17 if(cmp<0) p1+=l+1;
18 else p2+=l+1;
19 l=0;
20 p2+=(p2==p1);
21 }
22 }
23 return p1<p2?p1:p2;
24 }
25 int minpos(int len)
26 {
27 int p1=0,p2=1,l=0,i;
28 while(p1<len&&p2<len&&l<len)
29 {
30 int cmp=str[p1+l]-str[p2+l];
31 if(!cmp)
32 l++;
33 else
34 {
35 if(cmp>0) p1+=l+1;
36 else p2+=l+1;
37 l=0;
38 p2+=(p2==p1);
39 }
40 }
41 return p1<p2?p1:p2;
42 }
43 int gettimes(int start,int len)
44 {
45 int p,res=0;
46 memset(end,0,sizeof(end));
47 end[start+len-1]=1;
48 pre[start]=start-1;
49 for(p=start+1;p<2*len-1;p++)
50 {
51 pre[p]=pre[p-1];
52 while(pre[p]!=start-1&&str[pre[p]+1]!=str[p]) pre[p]=pre[pre[p]];
53 if(str[pre[p]+1]==str[p]) pre[p]++;
54 if(pre[p]!=start-1&&!end[p]) end[p]=end[pre[p]];
55 res+=end[p];
56 }
57 return res;
58 }
59 int main()
60 {
61 while(gets(str))
62 {
63 int len=strlen(str),i;
64 for(i=len;i<2*len-1;i++) str[i]=str[i-len];
65 str[2*len-1]='\0';
66 int p1=minpos(len);
67 int t1=gettimes(p1,len);
68 printf("%d %d ",p1+1,t1);
69 p1=maxpos(len);
70 t1=gettimes(p1,len);
71 printf("%d %d\n",p1+1,t1);
72 }
73 return 0;
74 }
75