HDU-3374:String Problem(KMP+最小表示法)

Give you a string with length N, you can generate N strings by left shifts. For example let consider the string “SKYLONG”, we can generate seven strings:
String Rank
SKYLONG 1
KYLONGS 2
YLONGSK 3
LONGSKY 4
ONGSKYL 5
NGSKYLO 6
GSKYLON 7
and lexicographically first of them is GSKYLON, lexicographically last is YLONGSK, both of them appear only once.
  Your task is easy, calculate the lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), its times, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.

Input   
Each line contains one line the string S with length N (N <= 1000000) formed by lower case letters. 
Output 
Output four integers separated by one space, lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), the string’s times in the N generated strings, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also. 
Sample Input
abcder
aaaaaa
ababab
Sample Output

1 1 6 1
1 6 1 6
1 3 2 3
概譯:一個字符串循環一下,每個位置都當一次頭(即循環同構),求最小和最大字典序的那兩個串的起始位置和總共出現次數。譯的不好,不過原文其實很好懂。

輸出:最小字典序的rank,出現次數,最大字典序的rank,出現次數

求rank是典型的用最小表示法來求,而求出現的次數是循環節的次數,沒有循環節就1次唄。求循環節需要使用到kmp算法中next數組的遞推過程。
至於kmp算法和最小表示法,都是字符串的相關算法。個人感覺講起來是很難懂的,不懂的同學希望能夠自學一下,按照博客上的思路及代碼自己畫一畫會好很多。這道題目和這兩個算法,AlphaWA也是聽了大佬的講課介紹然後新學的(被校友認出系列),剛開始肯定感覺不透徹,不熟悉原理,不過以後慢慢變強了就會加深理解了吧,學習的過程好像一般都是這樣子的(我胡扯的)。
代碼如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 1000010
using namespace std;

char s[maxn];
int Next[maxn];
	
void getnext(char *s,int len)//求next數組以獲得循環節長度 
{
	Next[0]=Next[1]=0;
	for(int i=1;i<len;i++)
	{
		int j=Next[i];
		while(j&&s[i]!=s[j]) j=Next[j];
		Next[i+1]=s[i]==s[j]?j+1:0;
	}
}
int express(char *s,int len,int flag)//最小表示法求最小字典序的循環同構的起始位置 
{
	int i=0,j=1,k=0;
	while(i<len&&j<len&&k<len)
	{
		int t=s[(i+k)%len]-s[(j+k)%len];
		if(flag)//最小表示 
		{
			if(!t) k++;
			else if(t>0) i=i+k+1,k=0;
			else j=j+k+1,k=0;
			if(i==j) j++;
		}
		else//最大表示 
		{
			if(!t) k++;
			else if(t<0) i=i+k+1,k=0;
			else j=j+k+1,k=0;
			if(i==j) j++;	
		}
	}
	return min(i,j);
}
int main()
{
	while(~scanf("%s",s))
	{
		int len=strlen(s);
		int rank1=express(s,len,1)+1,rank2=express(s,len,0)+1;//flag設爲1是求最小字典序,設爲0是求最大 
		getnext(s,len);//kmp裏面求next數組的方法 
		int cnt=len/(len-Next[len]);//據說個數就是循環節的個數,最大最小都是一樣的 
		printf("%d %d %d %d\n",rank1,cnt,rank2,cnt);
	}
	return 0;
} 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章