题目描述
Fernando 受雇于滑铁卢大学,负责完成该大学不久前开始的一个开发项目。在校园外,该大学希望为重要的外国游客和合作者建造具有代表性的平房街。
目前,这条街只建了一部分,它从湖岸开始,一直延伸到森林尽头。Fernando 的任务是通过在森林尽头建造更多的平房来完成这条街。所有现有的平房都坐落在街道的一侧,新的平房应该建在同一侧。这些平房有各种各样的类型,漆成各种各样的颜色。
在 Fernando 看来,整条街的布局有点混乱。他担心增加新平房后,它会看起来更加混乱。所以他想通过为新平房选择合适的颜色来增加一些排列顺序。当项目完成时,平房的整个颜色序列将是对称的,也就是说,从街道的两端观察时,颜色序列是相同的。
在其他问题中,Fernando 想知道,在满足平房颜色限制的情况下,他最少需要用来建造和适当染色才能完成项目的新平房数量。
简要题意
求使给定小写字母字符串成为回文串需在字符串末尾加入字母的最少数量。
输入格式
第一行包含一个整数 N (1≤N≤4×105)N (1≤N≤4×105),代表街道上现有平房的数量。
第二行包含一个由 NN 个小写字母(从 a
到 z
)组成的字符串,代表从湖岸开始的街道现有的平房颜色顺序,其中不同的字母表示不同的颜色。
输出格式
输出一个整数,代表满足 Fernando 要求的新平房的最少数量。
题解
1. 字符串处理
- 存储原始字符串长度
in = n
,用于后续计算。 - 创建原始字符串的副本
str_r = str
。 - 将原始字符串
str
反转。 - 将反转后的字符串
str
、一个分隔符"#"
和原始字符串的副本str_r
拼接在一起,形成新的字符串str
。 - 更新
n
为新字符串str
的长度。
2. 计算 LPS 数组
- 函数
computerLPS()
用于计算 LPS 数组。LPS 数组的定义是:lps[i]
表示str[0...i]
这个子串的最长相同前缀后缀的长度。 - KMP算法核心思想:
- 初始化
len = 0
和lps[0] = 0
。 - 从
i = 1
开始遍历拼接后的字符串str
。 - 如果
str[i] != str[len]
,则不断更新len = lps[len - 1]
,直到len
为0
或找到一个相等的字符。 - 如果
str[i] == str[len]
,则len
自增1
。 - 更新
lps[i] = len
。
- 初始化
3. 计算并输出结果
- 计算最小循环节的长度:
in - lps[str.size() - 1]
。其中in
是原始字符串的长度,lps[str.size() - 1]
是拼接后字符串的最后一个字符对应的 LPS 值。 - 输出计算得到的最小循环节的长度。
4. 原理说明
- 拼接字符串的原因:通过将反转后的字符串与原字符串拼接,可以利用KMP算法找到原字符串与反转字符串之间的最大公共前后缀。
- 计算最小循环节的原理:拼接后的字符串的最后一个字符对应的 LPS 值,表示反转后的字符串与原字符串的公共前后缀长度。用原字符串的长度减去这个公共前后缀长度,就可以得到最小循环节的长度。
例如,如果原字符串是 "abcabc",那么反转后的字符串是 "cbacba"。拼接后的字符串是 "cbacba#abcabc"。这个拼接后的字符串的 LPS 数组的最后一个值就是 3 ("abc")。原字符串长度是 6,6 - 3 = 3,所以最小循环节的长度是 3 ("abc")。
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int n;
string str;
int lps[800004];
void computerLPS(){
int len=0;
lps[0]=0;
for(int i=1;i<n;i++){
while(len>0&&str[i]!=str[len]){
len=lps[len-1];
}
if(str[i]==str[len]){
len++;
}
lps[i]=len;
}
}
int main(){
cin>>n;
cin>>str;
int in=n;
string str_r=str;
reverse(str.begin(),str.end());
str=str+"#"+str_r;
n=str.size();
computerLPS();
cout<<in-lps[str.size()-1];
return 0;
}