• Favorite Donut--hdu5442(2015年长春网选赛,kmp,最大表示法)


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5442

    打比赛的时候还没学kmp更没有学最大最小表示法,之后做完了kmp的专题,学了它们,现在再来做这道题,断断续续做了一段时间最终还是对了;

    题意:就是有一个甜圈由n个部分组成,每部分的甜度由一个小写字母来表示(z是最甜的,a是最不甜的),Lulu吃它的时候必须从一个部分开始,然后必须吃这部分相邻的部分,一直到把n部分吃完为止;所以她只有两种吃法:正序或逆序;但是她总是喜欢吃最甜的你,总是选择字典序最大的来吃当有多种字典序相等的时候选择开始下标最小的,当下标也相等的时候则选择正序;

    思路:求最大字典序我们可以用最大表示法来求,我们用s0来存两次串的正序,s1来存两次串的逆序,然后分别求出s0,s1中的最大字典序序列并分别存入a b中,但是b是逆序的,所以我们必须要求出来b串在s1中最先先出现的位置(最后面的位置);所以可以用kmp进行匹配;

    看代码吧

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define N 40200
    
    char s[N], s0[N], s1[N], a[N], b[N];
    int Next[N];
    
    int MAX_INDEX(char s[], int n)///最大表示法求最大字典序的起始下标;
    {
        int i = 0, j = 1, k = 0;
        while(i<n && j<n && k<n)
        {
            int t = s[(i+k)%n] - s[(j+k)%n];
            if(t==0)
                k++;
            else
            {
                if(t<0)
                    i = i+k+1;
                else
                    j = j+k+1;
                if(i==j)
                    j++;
                k = 0;
            }
        }
        return min(i, j);
    }
    
    void GetNext(char s[], int n)///求子串的Next数组;
    {
        int i=0, j=-1;
        Next[0] = -1;
        while(i<n)
        {
            if(j==-1 || s[i]==s[j])
                Next[++i] = ++j;
            else
                j = Next[j];
        }
    }
    
    int kmp(char sub[], int n, char Mum[], int m)///长度为n的子串在长度为m的母串中的位置,并返回开始匹配的下标,若没有返回-1;
    {
        int i=0, j=0;
        while(i<m)
        {
            if(j==-1 || Mum[i]==sub[j])
                i++,j++;
            else
                j = Next[j];
            if(j==n)
                return i-n+1;
        }
        return -1;
    }
    
    int main()
    {
        int T, dir,ans;
        scanf("%d", &T);
        while(T--)
        {
            memset(a, 0, sizeof(a));
            memset(b, 0, sizeof(b));
            memset(s0, 0, sizeof(s0));
            memset(s1, 0, sizeof(s1));
    
            int len;
            scanf("%d%s", &len, s);
    
            strcpy(s0, s);
            strcat(s0, s);///正着存两次放进s0;
            s0[2*len] = '';
    
            int j=0;
            for(int i=2*len-1; i>=0; i--)
                s1[j++] = s0[i];
            s1[j] = '';///倒着存两次放进s1;
    
            int Max_index1 = MAX_INDEX(s0, 2*len);
            int Max_index2 = MAX_INDEX(s1, 2*len);///用最大表示法求出最大字典序的下标;
    
            strncpy(a, s0+Max_index1, len);
            strncpy(b, s1+Max_index2, len);
            a[len] = ''; b[len] = '';///分别用字符串a,b来保存正序和逆序的最大字典序的字符;
    
            GetNext(b, len);
    
            int m = kmp(b, len, s1+Max_index2+1, 2*len-Max_index2-1);
            while(m!=-1 && m+Max_index2!=len)///下标不能是len;
            {
                Max_index2 += m;
                m = kmp(b, len, s1+Max_index2+1, 2*len-Max_index2-1);
            }///因为s1串是逆序的用最大表示法得到的下标在逆序中是最靠前的,然而在正序中确实最靠后的,所以要用kmp求出s1串中与b一样的串最后的位置所在;
    
            if(strcmp(a, b)>0)
                dir = 0, ans = Max_index1 + 1;
            else if(strcmp(a, b)<0)
                dir = 1, ans = len - Max_index2;
            else///当串一样时, 要考虑谁考前,结果就是谁;
            {
                if(Max_index1 + 1 <= len - Max_index2)
                    dir = 0, ans = Max_index1 + 1;
                else
                    dir = 1, ans = len - Max_index2;
            }
            printf("%d %d
    ", ans, dir);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    *三维数组的初始化及遍历三个for循环
    *二维数组的初始化
    用while判读循环语句1+1/2!+1/3!+...1/20!的和阶乘的计算方法 式:n!=n*(n-1)!
    求一组数组各个元素的和*
    *求一组数组各个元素的和*
    使用for循环输出杨辉三角-还是不懂得需要复习
    使用for循环输出空心的菱形的思路-还是没有办法理解
    Break用法再举例
    continue用来结束本次循环 break用来结束整个循环体
    LeetCode.1154-一年中的第几天(Day of the Year)
  • 原文地址:https://www.cnblogs.com/zhengguiping--9876/p/4869072.html
Copyright © 2020-2023  润新知