您的位置:首页 > 其它

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

2015-11-23 00:48 316 查看
题意:给出一个字符串s,如 abcdef,可以对字符串进行左移操作,于是就可以生成如下strlen个串:abcdef,bcdefa,cdefab,defabc……求这些串中 字典序小和最大的串 开头的字母在原串中的位置,如果有多个输出序号最小的位置,并且输出这样的串总共有多少个。

输出格式:posmin countmin posmax countmax

收获:

1.用kmp找循环节。

(仔细分析,我们会发现countmin=countmax。因为向左移动后如果出现相同的串,说明这是一个循环,接着第二个出现的相同的串移动会有相同的结果。)

所谓循环节就是:abcabcabc 循环节就是3。

首先我们需要知道这个串有没有循环节:

我们知道nxt
表示的是以最后一个字符结尾的最长前缀的长度(kmp可知)。如果这个串有循环节,则s[0~(len-nxt
-1)]必定为这个串的循环节(即上例中的abc,可以任意举含循环节的例子验证),则 len % (len-nxt
)==0,反过来推亦成立,所以这个为 s串有循环节的充要条件。

根据上诉所说,我们就可以知道循环节的长度为(len-nxt
)。

既然知道了长度,那么重复出现的个数=len/(len-nxt[n])。

2.找到字典序最大and最小的串。

以找最小为例。

朴素想法是通过两个指针进行 O(n^2) 枚举:

i 为枚举串的串头,j表示比较串的串头。

初始:i=0;j=1;

分3种情况:

==s[i]< s[j] j++;

==s[i]> s[j] i=j;j=j+1;

==s[i]==s[j] 就利用k 来接着i,j向后面比较 s[i+k] s[j+k].

====s[i+k]==s[j+k] k++

====s[i+k]< s[j+k] j++ ;k=0

====s[i+k]> s[j+k] i=j ;j=j+1; k=0

最后答案为 i;

对于 bbbbbbba 这样的串呢,我们可以轻松知道它的复杂度为n^2;

于是我们需要优化,而i+1~j-1其实都是比s[i]大的元素了。

==s[i]< s[j] j++;

==s[i]> s[j] i=j;j=j+1;

==s[i]==s[j] 就利用k 来接着i,j向后面比较 s[i+k] s[j+k].

====s[i+k]==s[j+k] k++

====s[i+k]< s[j+k] j=j+k+1 ;k=0

====s[i+k]> s[j+k] i=i+k+1;;k=0

只有加粗这一行不同。i,j位置的生成其实本身就继承了一些东西,所以可以直接转移到i+K+1。

因为i+k总共只会吧n扫一遍,j+k总共只会把n扫一遍 所以并起来(并不是相加) 复杂度就是O(N)的。

3.kmp姿势问题

我一直以来写getnxt的姿势都是

nxt[0]=-1;nxt[1]=0;int p;
for(int i=2;i<n;i++)
{
p=i-1;
while(p>0 && sz[nxt[p]]!=sz[i-1])
p=nxt[p];
nxt[i]=nxt[p]+1;
}


其实本身没错。。。只是不能算出nxt[n]。。。。��

所以从今以后我要换一种比较普遍而简洁的写法。

因为循环节的原因,这题又一次加深了我对kmp的nxt数组理解。

4.

字符串函数 strcat()又把我坑了。。。一开始我的打算是直接把原串扩为两倍。。。这样方便一点,这样T。。于是我又对它的复杂度产生了怀疑,并且没有搜到。

最后还是只能mod 来处理。

//  Created by ZYD in 2015.
//  Copyright (c) 2015 ZYD. All rights reserved.
//

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define Size 100000
#define ll long long
#define mk make_pair
#define pb push_back
#define mem(array) memset(array,0,sizeof(array))
typedef pair<int,int> P;
int nxt[2000005],n;
char sz[1000005];
int kmp()
{
int i,j,p;
nxt[0]=-1;
nxt[1]=0;
for(int i=2;i<n;i++)
{
p=i-1;
while(p>0 && sz[nxt[p]]!=sz[i-1])
p=nxt[p];
nxt[i]=nxt[p]+1;
}
return 0;
}
int getmin()
{
int i,j,k,tmp;
i=0;j=1;k=0;
// strcat(sz,sz);
while(i<n && j<n && k<n)
{
tmp=sz[(i+k)%n]-sz[(j+k)%n];
if(tmp==0)
{
k++;
}
else
{
if(tmp>0)
{
i=i+k+1;
}
else j=j+k+1;
if(i==j)
j++;
k=0;
}
}
return min(i,j);
}
int getmax()
{
int i,j,k,tmp;
i=0;j=1;k=0;
while(i<n && j<n && k<n)
{
tmp=sz[(i+k)%n]-sz[(j+k)%n];
if(tmp==0)
{
k++;
}
else
{
if(tmp<0)
{
i=i+k+1;
}
else j=j+k+1;
if(i==j)
j++;
k=0;
}
}
return min(i,j);
}

int main()
{
freopen("in.txt","r",stdin);
while(~scanf("%s",sz))
{
n=strlen(sz);
mem(nxt);
kmp();
int r=n-nxt
;
if(n % r==0) r=n/r;
else r=1;
// for(int i=0;i<n;i++) cout<<sz[i]<<" ";cout<<endl;
// for(int i=0;i<n;i++) cout<<nxt[i]<<" ";cout<<endl;
printf("%d %d %d %d\n",getmin()+1,r,getmax()+1,r);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: