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

题意:给出一个字符串s,如 abcdef,可以对字符串进行左移操作,于是就可以生成如下strlen个串:abcdef,bcdefa,cdefab,defabc……求这些串中 字典序小和最大的串 开头的字母在原串中的位置,如果有多个输出序号最小的位置,并且输出这样的串总共有多少个。
输出格式:posmin countmin posmax countmax
收获:
1.用kmp找循环节。
(仔细分析,我们会发现countmin=countmax。因为向左移动后如果出现相同的串,说明这是一个循环,接着第二个出现的相同的串移动会有相同的结果。)
所谓循环节就是:abcabcabc 循环节就是3。
首先我们需要知道这个串有没有循环节:
我们知道nxt[n]表示的是以最后一个字符结尾的最长前缀的长度(kmp可知)。如果这个串有循环节,则s[0~(len-nxt[n]-1)]必定为这个串的循环节(即上例中的abc,可以任意举含循环节的例子验证),则 len % (len-nxt[n])==0,反过来推亦成立,所以这个为 s串有循环节的充要条件。
根据上诉所说,我们就可以知道循环节的长度为(len-nxt[n])。
既然知道了长度,那么重复出现的个数=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[n];
        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;
}


你可能感兴趣的:(KMP)