您的位置:首页 > 其它

3374 String Problem

2016-06-05 22:36 239 查看


String Problem

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 2521    Accepted Submission(s): 1063


Problem Description

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

题意大意:给你一个字符串,求它的同构字符串中字典序最小的和字典序最大的起始位置以及出现的次数。同构字符串指的是一个字符串通过循环移位得到的字符串,题目中也有解释,应该是可以看懂的。

思路:

(一)求出它的同构循环串,用map存储出现的次数,然后排序,就可以求出答案了。代码如下,提交超内存。
<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#define LL long long
using namespace std;
const int maxn = 1000000+5;
struct node{
string s;
int r;
}ss[maxn];
bool cmp(node x,node y)
{
if(x.s!=y.s) return x.s<y.s;
return x.r<y.r;
}
int main()
{
cin.sync_with_stdio(false);
string a,b;
int la,c;
while(cin>>a){
map<string,int>m;
la=a.size();
c=0;
m[a]++;
ss[c].s=a;
ss[c++].r=1;
for(int i=1;i<la;i++){
b=a.substr(i)+a.substr(0,i);
//cout<<"b="<<b<<endl;
ss[c].s=b;
ss[c++].r=i+1;
m[b]++;
}
sort(ss,ss+c,cmp);
//for(int i=0;i<c;i++) cout<<ss[i].s<<' '<<ss[i].r<<endl;
cout<<ss[0].r<<' '<<m[ss[0].s]<<' ';
int t=la-1;
while(ss[t].s==ss[t-1].s){
t--;
if(t==0) break;
}
cout<<ss[t].r<<' '<<m[ss[t].s]<<endl;
}
return 0;
}</span>

(二)用最小表示法求字典序最小的字符串,最大表示法求字典序最大的字符串,next数组求出现的次数。

最小表示法:已知了起始坐标就可以唯一确定同构循环串,分别用i和j表示两个不同的循环串的起始位置,k表示比较的长度,解释一下下面的语句吧

if(s[(i+k)%ls]>s[(j+k)%ls]) i=i+k+1; //因为s[i]~s[(i+k-1)%ls]==s[j]~s[(j+k-1)%ls],所有当s[(i+k)%ls]>s[(j+k)%ls]时可以得出i~(i+k-1)%ls中以任意坐标开始的都要比j~(j+k-1)%ls的要大,所有i可以直接等于i+k+1

next数组可以求出一个字符串的循环节,比如字符串s,ls=s.size(),那么s的循环节的长度len=ls-next[ls],所有出现的次数为ls/len。 这个AC
<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#define LL long long
using namespace std;
const int maxn = 1000000+5;
int Next[maxn];
void getNext(string s)
{
int ls=s.size();
Next[0]=-1;
Next[1]=0;
for(int i=2;i<=ls;i++){
if(s[i-1]==s[Next[i-1]]) Next[i]=Next[i-1]+1;
else{
int t=Next[i-1];
while(s[i-1]!=s[t]){
t=Next[t];
if(t==-1) break;
}
Next[i]=t+1;
}
}
}
int getMin(string s)
{
int ls=s.size();
int i=0,j=1,k=0;
while(i<ls && j<ls && k<ls){   //k<ls 一定要写,因为当s=aaaaa时不写会死循环
if(s[(i+k)%ls]==s[(j+k)%ls]) k++;
else{
if(s[(i+k)%ls]>s[(j+k)%ls]) i=i+k+1;
else j=j+k+1;
if(i==j) j++;
k=0;
}
}
return min(i,j);
}
int getMax(string s)
{
int ls=s.size();
int i=0,j=1,k=0;
while(i<ls && j<ls && k<ls){
if(s[(i+k)%ls]==s[(j+k)%ls]) k++;
else{
if(s[(i+k)%ls]>s[(j+k)%ls]) j=j+k+1;
else i=i+k+1;
if(i==j) j++;
k=0;
}
}
return min(i,j);
}
int main()
{
cin.sync_with_stdio(false); //记得用c++要加这一句,因为不加会超时
string s;
int ls,len,smin,smax;
while(cin>>s){
getNext(s);
ls=s.size();
len=ls-Next[ls];
smin=getMin(s)+1;
smax=getMax(s)+1;
cout<<smin<<' '<<ls/len<<' '<<smax<<' '<<ls/len<<endl;
}
return 0;
}</span>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: