hdu 3374 String Problem(字符串最小最大表示法+kmp)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3374
题目大意:给你一个字符串,让你分别求出最小和最大表示法的起始位置,如果有多个,就求出最左边那个,然后分别求出它在同构串中的出现次数。
思路:最小最大表示法,两个一样,稍微变一下就好,然后找到位置后,最直接的就是直接做kmp,然后求出次数,由于扩大了两倍,最后那个长度为n的
字符串不算,所以kmp匹配的时候直接是 < len_t-1 就好。之后我去网上看了一下,发现很多是不做kmp的,直接用fail数组的性质,因为是同构串,看它的
最小循环节就好,其实重复次数两个是一样的,都等于最小循环节的出现次数(如果循环节存在的话,否则为1)。

直接kmp的代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 1000111;

char str[MAXN<<1],s[MAXN];

int fail[MAXN];

void get_fail(char *p)
{
    fail[0] = -1;
    int j = -1;
    int len = strlen(p);
    for(int i = 1;i < len;i++)
    {
        while(j >= 0 && p[i] != p[j+1]) j = fail[j];
        if(p[i] == p[j+1]) j++;
        fail[i] = j;
    }
}

int kmp(char *t,char *p)
{
    get_fail(p);
    int len_t = strlen(t);
    int len_p = strlen(p);
    int j = -1;
    int ans = 0;
    for(int i = 0;i < len_t-1;i++)
    {
        while(j >= 0 && t[i] != p[j+1]) j = fail[j];
        if(t[i] == p[j+1]) j++;
        if(j == len_p-1)
        {
            ans++;
            j = fail[j];
        }
    }
    return ans;
}

int main()
{
    while(~scanf("%s",str))
    {
        int n = strlen(str);
        for(int i = 0;i < n;i++)
            str[n+i] = str[i];
        str[n+n] = '\0';
        //puts(str);
        int p1 = 0,p2 = 1,mat = 0;
        while(p1 < n && p2 < n)
        {
            if(str[p1+mat] == str[p2+mat])
            {
                mat++;
                if(mat == n)
                    break;
            }
            else
            {
                if(str[p1+mat] < str[p2+mat])
                    p2 += mat+1;
                else p1 += mat+1;
                if(p1 == p2) p2++;
                mat = 0;
            }
        }
        int ans1 = min(p1,p2);
        for(int i = 0;i < n;i++)
            s[i] = str[i+ans1];
        s[n] = '\0';
        int times1 = kmp(str,s);

        p1 = 0,p2 = 1,mat = 0;
        while(p1 < n && p2 < n)
        {
            if(str[p1+mat] == str[p2+mat])
            {
                mat++;
                if(mat == n)
                    break;
            }
            else
            {
                if(str[p1+mat] < str[p2+mat])
                    p1 += mat+1;
                else p2 += mat+1;
                if(p1 == p2) p2++;
                mat = 0;
            }
        }
        int ans2 = min(p1,p2);
        for(int i = 0;i < n;i++)
            s[i] = str[i+ans2];
        s[n] = '\0';
        int times2 = kmp(str,s);
        printf("%d %d %d %d\n",ans1+1,times1,ans2+1,times2);
    }
    return 0;
}


利用fail的代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 1000111;

char str[MAXN<<1],s[MAXN];

int fail[MAXN];

void get_fail(char *p)
{
    fail[0] = -1;
    int j = -1;
    int len = strlen(p);
    for(int i = 1;i < len;i++)
    {
        while(j >= 0 && p[i] != p[j+1]) j = fail[j];
        if(p[i] == p[j+1]) j++;
        fail[i] = j;
    }
}

int main()
{
    while(~scanf("%s",str))
    {
    	get_fail(str);
        int n = strlen(str);
        for(int i = 0;i < n;i++)
            str[n+i] = str[i];
        str[n+n] = '\0';
        //puts(str);
        int p1 = 0,p2 = 1,mat = 0;
        while(p1 < n && p2 < n)
        {
            if(str[p1+mat] == str[p2+mat])
            {
                mat++;
                if(mat == n)
                    break;
            }
            else
            {
                if(str[p1+mat] < str[p2+mat])
                    p2 += mat+1;
                else p1 += mat+1;
                if(p1 == p2) p2++;
                mat = 0;
            }
        }
        int ans1 = min(p1,p2);
        p1 = 0,p2 = 1,mat = 0;
        while(p1 < n && p2 < n)
        {
            if(str[p1+mat] == str[p2+mat])
            {
                mat++;
                if(mat == n)
                    break;
            }
            else
            {
                if(str[p1+mat] < str[p2+mat])
                    p1 += mat+1;
                else p2 += mat+1;
                if(p1 == p2) p2++;
                mat = 0;
            }
        }
        int ans2 = min(p1,p2);
        int len = n-1-fail[n-1];
        int times = 1;
        if(n%len == 0)
        	times = n/len;
        printf("%d %d %d %d\n",ans1+1,times,ans2+1,times);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值