洛谷P2799 国王的墨镜 题解 递归
题目链接:洛谷P2799
题目描述
国王有一个魔镜,可以把任何接触镜面的东西变成原来的两倍——只是,因为是镜子嘛,增加的那部分是反的。比如一条项链,我们用AB来表示,不同的字母表示不同颜色的珍珠。如果把B端接触镜面的话,魔镜会把这条项链变为ABBA。如果再用一端接触的话,则会变成ABBAABBA(假定国王只用项链的某一端接触魔镜)。给定最终的项链,请编写程序输出国王没使用魔镜之前,最初的项链可能的最小长度。
输入格式
只有一个字符串,由大写英文字母组成(字母数<=100000),表示最终的项链。
输出格式
只有一个整数,表示国王没使用魔镜前,最初的项链可能的最小长度。
样例输入
ABBAABBA
样例输出
2
题目分析
本题涉及算法:递归。
首先你思考一下,如果给你一个长度为 \(n\) 的字符串 \(s\),你需要判断它是否是轴对称,并且还不能是奇数的:
- 如果 \(n\) 为奇数,那么 \(n\) 就是最小长度(因为奇数无法作为镜子的两边进行映射),直接返回 \(n\) 就是最小长度;
- 然后考虑一下 \(n\) 为偶数的情况,这种情况下我们需要从 \(s[0]\) 遍历到 \(s[n/2-1]\) 遍历 \(s[i]\) ,比较 \(s[i]\) 和 \(s[n-1-i]\) ,如果 \(s[i] != s[n-1-i]\) ,说明它不是轴对称的,直接返回 \(n\) 就是最小长度。
如果 \(n\) 为偶数并且 \(s[0..n-1]\) 是轴对称的,那么我们继续去判断 \(s[0..n-1]\) 的前 \(n/2\) 个元素组成的子串;
如果 \(n/2\) 为偶数并且 \(s[0..\frac{n}{2}-1]\) 是轴对称的,那么我们继续去判断 \(s[0..\frac{n}{2}-1]\) 的前 \(n/4\) 个元素组成的子串;
……
如是循环,直到我们找到一个 \(n\) 为奇数或者它不是轴对称的。这个时候对应的 \(n\) 就是我们想要的最小的长度。
我们可以开一个函数 int check(string s, int n)
,它用于判断字符串 \(s\) 的前 \(n\) 的最小长度,
如果 \(n\) 是奇数 或者 \(s\) 不是轴对称的,该函数直接返回 \(n\) ,说明 \(n\) 就是它的最小程度;
否则,它是偶数长度并且是轴对称的,那么它返回的结果就是 check(s, n/2)
的值。
按这种方法进行递归,最终能找到我们想要的答案。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
// check函数用于返回s的前n个字符能够组成的最小长度
int check(string s, int n) {
if (n % 2) return n; // n为奇数时,直接返回n
for (int i = 0; i < n/2; i ++)
if (s[i] != s[n-1-i]) // s不轴对称,直接返回n
return n;
return check(s, n/2); // 递归进s的前n/2个字符进行判断
}
string s;
int n;
int main() {
cin >> s;
n = s.length();
cout << check(s, n) << endl;
return 0;
}