您的位置:首页 > 其它

O - String Problem KMP 字符串最小表示法

2017-04-09 22:16 363 查看
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.OutputOutput 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

第一次的超时代码


#include<iostream>
#include<set>
#include<map>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
/*
找一个字符串所有左移产生的字符串中(字符串长度个)
第一个字典序最小的序列所在位置 和出现次数(字符串长度除以最小循环节长度)
第一个字典序最大的序列所在位置 和出现次数
字典序最小或者最大 所在位置 就是向左移动次数
从前往后遍历 遇到比min小的字符就设置为起点,有相等的就循环比较后面的元素(这里时间复杂度n)
总的时间复杂度 n2
*/
#define MAXN 1000001
char s[MAXN];
int Next[MAXN];
void kmp_pre(int l)
{
int j,k;
j=0;k = Next[0] = -1;
while(j<l)
{
if(k==-1||s[j]==s[k])
Next[++j] = ++k;
else
k = Next[k];
}
}
int main()
{
while(scanf("%s",s)!=EOF)
{
int len = strlen(s),Min=0,Max=0;
kmp_pre(len);
for(int i=1;i<len;i++)
{
if(s[i]<s[Min])
Min = i;
else if(s[i]==s[Min])
{
for(int j=1;j<len;j++)
{
if(s[(i+j)%len]<s[(Min+j)%len])
{
Min = i;
break;
}
}
}
if(s[i]>s[Max])
Max = i;
else if(s[i]==s[Max])
{
for(int j=1;j<len;j++)
{
if(s[(i+j)%len]>s[(Max+j)%len])
{
Max = i;
break;
}
}
}
}
printf("%d %d %d %d\n",Min+1,len/(len-Next[len]),Max+1,len/(len-Next[len]));
}
}


#include<iostream>
#include<set>
#include<map>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
/*
找一个字符串所有左移产生的字符串中(字符串长度个)
第一个字典序最小的序列所在位置 和出现次数(字符串长度除以最小循环节长度)
第一个字典序最大的序列所在位置 和出现次数
枚举方法求字典序最大或者最小字符串的算法n方太蠢了,
以最小为例:原因在于i失配的时候移动距离太小,没有充分利用
已知条件,比如i-i+k之间的数字显然是大于或者等于i的,所以这些部分没必要再枚举一遍了!直接让i=i+k
*/
#define MAXN 1000001
char s[MAXN];
int Next[MAXN];
void kmp_pre(int l)
{
int j,k;
j=0;k = Next[0] = -1;
while(j<l)
{
if(k==-1||s[j]==s[k])
Next[++j] = ++k;
else
k = Next[k];
}
}
int FindMin(char s[],int l)
{
int i=0,j=1,k=0;
while(i<l&&j<l&&k<l)
{
if(s[(i+k)%l]==s[(j+k)%l])
k++;
else if(s[(i+k)%l]>s[(j+k)%l])
{
i = i+k+1;
k = 0;
}
else
{
j = j+k+1;
k = 0;
}
if(i==j)
j++;
}
return min(i,j);
}
int FindMax(char s[],int l)
{
int i=0,j=1,k=0;
while(i<l&&j<l&&k<l)
{
if(s[(i+k)%l]==s[(j+k)%l])
k++;
else if(s[(i+k)%l]<s[(j+k)%l])
{
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)!=EOF)
{
int len = strlen(s);
kmp_pre(len);
int Max = FindMax(s,len),Min = FindMin(s,len);
printf("%d %d %d %d\n",Min+1,len/(len-Next[len]),Max+1,len/(len-Next[len]));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: