题目描述
大神分析
本题出现了一个新概念:字典序。所谓字典序,就是字符串在字典中的顺序。一般地, 对于两个字符串,从第一个字符开始比较,当某一个位置的字符不同时,该位置字符较小的 串,字典序较小(例如,abc比bcd小);如果其中一个字符串已经没有更多字符,但另一个 字符串还没结束,则较短的字符串的字典序较小(例如,hi比history小)。字典序的概念可 以推广到任意序列,例如,序列1, 2, 4, 7比1, 2, 5小。
学会了字典序的概念之后,本题就不难解决了:就像"求n个元素中的最小值"一样,用 变量ans表示目前为止,字典序最小串在输入串中的起始位置,然后不断更新ans。
代码注释(非常啰嗦)
#include <stdio.h>
#include <string.h>
#define maxn 105
// 环状串 s 的表示法 p 是否比表示法 q 的字典序小
// 比较第 p 个字符和第 q 个字符谁的字典序小
int less(const char * s, int p, int q) {
// 当前字符串 s 的长为 n
int n = strlen(s);
// 接下来以 n 为一个周期,依次从该周期的第一个开始比较大小
// 由题目分析,我们知道,每一个长度为 n 的环状串都有 n 种表示法(一般情况下)
// 而所谓的 n 种方法,每一种都是以该串的一个字母开始,依次输出 n 个字母
// 后得到的。
// 现在就是先从每个表示方法的第一个字母开始比较。
// 当调用这个函数时,传进来的 p 和 q 就是串 s 的两种表示方法中的首字母
for(int i = 0; i < n; i++) {
// 依次比较相对顺序上的字母,谁的字典序小
// 当两个位置上的字母不同时,一定可以比较出大小
if(s[(p + i) % n] != s[(q + i) % n])
// 如果 s[(p + i) % n] < s[(q + i) % n] 成立
// 该 return 值为 1
// 否则 return 返回 0
return s[(p + i) % n] < s[(q + i) % n];
// 另外当 if 条件不成立,表示当前比较的两个字母一样,会通过 i++, 比较下一对
}
// 如果循环执行到这里,说明相对位置上的字母都是一样的,也 return 返回 0
return 0; // 相等
}
int main() {
// T 为 将要输入的串的个数
int T;
// 输入的串保存在 s 数组中
char s[maxn];
scanf("%d", &T);
while(T--) {
scanf("%s", s);
// ans 用来记录,到目前位置,找到的最小串(字典序最小的串),在用户输入的串中
// 的起始位置,之后会不断更新 ans
int ans = 0;
int n = strlen(s);
for(int i = 1; i < n; i++)
// ans = 0 时,
// i = 1
// 即,从 s 串的第 ans 个字符后的第一个字符开始,依次与当前的 ans 项上的字符比较
// 如果 less 返回值为 1, 说明以第 i 项开始的串的字典序相对较小
// 则更新 ans,保存当前 “最小串” 首字母在 s 中的序号 i
// 如果 less 返回 0,则表示用第 i 项作为首字母表示的串的字典序
// 大于或等于相应的用 ans 表示的串
// 所以不执行操作,即,不更新 ans
if(less(s, i, ans)) ans = i;
// 通过循环后找到了满足条件的 ans(ans储存者最小串首字母)
for(int i = 0; i < n; i++) {
// 依次输出(从第 ans 项开始,输出 n 个值)
// 考虑到 会有输出到最后一位还没输出结束的情况,
// 这个时候 ans + i 已经等于 n - 1 (即,ans + i 已经到了最后一项了)
// 又要返回第 0 位,继续输出的话,
// 用 取模(取余)的方法,可以很好解决这个问题。% 号为 取余的意思(罗里吧嗦……)
putchar(s[(ans + i) % n]);
}
putchar('\n');
}
return 0;
}
启发启示
这里的 % 号取模是亮点。现在还处于欣赏阅读代码的阶段,自己不好写,再看看抄抄吧