HDU 5442 KMP + 最小表示法

HDU 5442

题目链接:

题意:

一个字符串里,从任意起点出发顺时针或者逆时针遍历字符串得到一个新的字符串。问得到的字典序最大的字符串。

存在多个相同的字符串时,输出起点最小的;起点相同时,优先输出顺时针。

思路:

最小表示法 + KMP

顺时针,直接用最小表示法得到答案。

逆时针,把串翻转。先用最小表示法得到最大字典序的字符串A,再用KMP得到离起点最远的点匹配A

然后就完了。

 

 

最小表示法,On)。开两个指针,具体网上有证明。

 

KMP。有fail表示长度和序号的两种写法,注意不要搞混。

源码:

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <cmath>

#include <algorithm>

#include <iostream>

#include <string>

#include <queue>

using namespace std;

const int MAXN = 20000 * 2 + 5;

char str1[MAXN], str2[MAXN];

char P[MAXN], T[MAXN];

int Minpre(char *s, int len)

{

    int i, j, k;

    i = 0;

    j = 1;

    while(i < len && j < len){

        for(k = 0 ; k < len ; k++)  if(s[(k + i) % len] != s[(k + j) % len])    break;

        if(k == len)    break;

        if(s[(k + i) % len] > s[(k + j) % len]) i = k + i + 1;

        else    j = k + j + 1;

        if(i == j)  j++;

    }

    return min(i, j);

}

int fail[MAXN];

void failure(char *s, int len)

{

    fail[0] = 0;

    int j = 0;

    for(int i = 1 ; i < len ; i++){

        while(j && s[i] != s[j])    j = fail[j - 1];

        if(s[i] == s[j])    j++;

        fail[i] = j;

    }

}

int KMP(char *P, char *T, int len1, int len2)

{

    failure(P, len1);

    int u, v;

    u = v = 0;

    int ans = 0;

    while(v + len1 - u <= len2){

        while(u && P[u] != T[v]) u = fail[u - 1];

        if(P[u] == T[v])    u++;

        if(u == len1){

            u = fail[u - 1];

            ans = v;

        }

        v++;

    }

    return ans;

}

int main()

{

//    freopen("1006.in", "r", stdin);

    int t;

    scanf("%d", &t);

    while(t--){

        int n;

        scanf("%d", &n);

        scanf("%s", str1);

//        printf("str1 = %s\n", str1);

        for(int i = 0 ; i < n ; i++)

            str1[i] = (25 - str1[i] + 'a') + 'a';

        int ans1 = Minpre(str1, n);

        for(int i = 0 ; i < n ; i++)

            str2[i] = str1[n - i - 1];

        str2[n] = '\0';

        int ans2 = Minpre(str2, n);

        for(int i = 0 ; i < n ; i++)

            P[i] = str2[(i + ans2) % n], T[i] = T[i + n] = str2[i];

        P[n] = '\0', T[n * 2] = '\0';

        int temp = KMP(P, T, n, n * 2 - 1);

//        printf("str1 = %s, str2 = %s\n", str1, str2);

//        printf("P = %s, T = %s\n", P, T);

//        printf("temp = %d", temp);

        temp -= n - 1;

//        if(temp < n && temp > ans2) ans2 = temp;

        ans2 = n - 1 - temp;

//        printf("ans1 = %d, ans2 = %d\n", ans1, ans2);

        int flag = 0;

        for(int i = 0 ; i < n ; i++){

            int t1 = (i + ans1) % n;

            int t2 = ((ans2 - i) % n + n ) % n;

//            printf("t1 = %d, t2 = %d, str1[t1] = %c, str2[t2] = %c\n", t1, t2, str1[t1], str1[t2]);

            if(str1[t1] > str1[t2]){

                flag = -1;  break;

            }

            else if(str1[t1] < str1[t2]){

                flag = 1;   break;

            }

        }

        ans1++, ans2++;

//        printf("ans1 = %d, ans2 = %d\n", ans1, ans2);

        if(flag == 1){

            printf("%d 0\n", ans1);

        }

        else if(flag == -1){

            printf("%d 1\n", ans2);

        }

        else{

            if(ans1 <= ans2)

                printf("%d 0\n", ans1);

            else

                printf("%d 1\n", ans2);

        }

    }

    return 0;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值