• 【题解】最强阵容


    题目描述

            拿着新换来的英雄卡,小李满心欢喜的准备和同学们PK一下。他们的游戏规则非常简单,双方把自己的牌绕成一圈,然后指定一个起点,从该张牌开始顺时针方向往后取,谁取出的字符串字典序更小(从左到右开始比较,碰到第一个不一样的字符进行比较,比较规则为a<b<...<z)谁将获得胜利。具体规则可参考样例。虽然现在小李的牌已经很好了,但是你能不能帮他快速算出起始位置,使得他能够派出最强阵容。

    输入格式

            第一行,一个整数n,表示共有n张牌。

            第二行共n个用一个空格隔开的小写字母,表示给定的一圈牌起始序列。

    输出格式

            仅一个整数,能获得最小字典序字符串的起点位置。如果有多个位置开始的字符串一样,则输出最小的那个位置,且第一个位置从1开始。

    输入样例

    4

    b c a b

    输出样例

    3

     

    数据规模

            对于100%的数据,1≤n≤30000。

    题解

            显然,就是要比较$n$个长度为$n$的子串,朴素做法的最坏时间复杂度为$O(n^{2}) $。显然需要优化。

            我们设当前最小子串的第一个字符位置为$ans$, 当前枚举到的子串的第一个字符位置为$i$。

            此时,假设字符串中的$[ans, ans + j]$与$[i, i + j]$相同,而第$ans + j + 1$位不等于第$i + j + 1$位。如果为小于,显然$ans$仍为最优答案,否则更新$ans = i$,然后继续枚举$i$即可。

            关键是下一个$i$的取值为什么?其实我们可以令$i = i + j + 1$。因为$[ans, ans + j] = [i, i + j]$,所以$[i, i + j]$其实不用枚举了,直接枚举$i + j + 1$即可。这样就可以做到$O(n) $。

    #include <iostream>
    #include <cstdio>
    
    #define MAX_N (30000 + 5) 
    
    using namespace std;
    
    int n;
    char s[MAX_N << 1];
    int ans = 1;
    
    int main() 
    {
        scanf("%d", &n); 
        for(register int i = 1; i <= n; ++i) 
        {
            getchar();
            s[i] = s[i + n] = getchar();
        }
        for(register int i = 2; i <= n; ++i) 
        {
            for(register int j = 0; j < n; ++j) 
            {
                if(s[ans + j] != s[i + j]) 
                {
                    if(s[ans + j] > s[i + j]) ans = i;
                    i += j;
                    break;
                }
            }
        }
        printf("%d", ans);
        return 0;
    }
    参考程序
  • 相关阅读:
    10年学到的编程经验总结
    高效阅读源代码指南
    一行代码解决各种IE兼容问题,IE6,IE7,IE8,IE9,IE10
    如何用git将项目代码上传到github
    关于[].slice.call(arguments, 1) 的思考
    前端笔试题(一)
    2017年第一波JavaScript前端面试题
    2017前端面试题之综合篇(1)
    2017前端面试题之Js篇(1)
    前后端分离后各自职责
  • 原文地址:https://www.cnblogs.com/kcn999/p/11222256.html
Copyright © 2020-2023  润新知