CF ECR92 C. Good String
题目中定义了字符串 a 1 a 2 … a n a_1a_2\dots a_n a1a2…an的左旋转 a 2 a 3 … a n a 1 a_2a_3\dots a_na_1 a2a3…ana1和右旋转 a n a 1 a 2 … a n − 1 a_na_1a_2\dots a_{n-1} ana1a2…an−1,然后定义了一个Good String
的东西,左旋转和右旋转是同一个字符串,即 a 2 a 3 … a n a 1 = a n a 1 a 2 … a n − 1 a_2a_3\dots a_na_1=a_na_1a_2\dots a_{n-1} a2a3…ana1=ana1a2…an−1,现在给定一个字符串,计算这个字符串经过删减后可以得到一个Good String
需要删减的最小的字符的数量.输入的输入格式为,先输入一个整数T
,表示测试的数量,然后输入T
个连续的仅由0~9
构成的字符串s
.输出需要删减的最小的字符串的数量.输入数据的规模:
1 ≤ t ≤ 1000 , 2 ≤ ∣ s ∣ ≤ 2 ⋅ 1 0 5 1 \leq t \leq 1000, 2 \leq |s| \leq 2 \cdot 10^5 1≤t≤1000,2≤∣s∣≤2⋅105
根据上面那个Good String
的定义,可以得到对于一个Good String
b 1 b 2 ⋯ b k b_1b_2\cdots b_k b1b2⋯bk,只可能存在以下两种情况:
Good String
,对于一个Good String
x y x y ⋯ x y xyxy\cdots xy xyxy⋯xy来说,左旋转之后得到的是 y x y ⋯ x y x yxy\cdots xyx yxy⋯xyx,有旋转之后得到是 y x y x y ⋯ x yxyxy\cdots x yxyxy⋯x旋转实际上是把原来奇偶位上的数字进行了一个简单的变换,原来奇数位的数字变成偶数位的,偶数位的变成奇数位的.如果不满足 k k%2=0 k的话, x y x y ⋯ x y x xyxy\cdots xyx xyxy⋯xyx,左旋转之后是 y x y ⋯ x y x x yxy\cdots xyxx yxy⋯xyxx,二右旋转则是 x x y x y ⋯ x y xxyxy\cdots xy xxyxy⋯xy,这样的两个字符串就不相等了.所以可以先从 a 1 a 2 ⋯ a n a_1a_2\cdots a_n a1a2⋯an中计算满足上面任意一个Good String
条件可以得到的最长Good String
的长度,然后用字符串总长减去这个最长的Good String
的长度得到的就是需要删除的最少的字符的数量.
#include
using namespace std;
typedef long long ll;
const int N = 2e5+5;
const int M = 10;
int buf[N];
int count_one(size_t n, int v){
int ans = 0;
for(auto i = 0; i < n; ++i){
if(buf[i] == v){
++ans;
}
}
return ans;
}
int count_two(size_t n, int x, int y){
int ans = 0;
for(auto i = 0; i < n; ++i){
if( ans & 1){
// 上一个x后面出现的y
if(buf[i] == y){
++ans;
}
}else{
// 最初的x或者上一个y后面的x
if(buf[i] == x){
++ans;
}
}
}
// 最后得到的ans是xyxy...(xy)的个数,可能存在末尾是单独一个x的情况
if( ans & 1)
--ans;
return ans;
}
void solve(size_t n){
int ans = 0;
// 计算xxx出现的次数
for(int i = 0; i < M; ++i){
ans = max(ans, count_one(n, i));
}
// 计算xyxy...出现的次数,两者取最大值
for(int i = 0; i < M; ++i){
for(int j = 0; j < M; ++j){
if( i != j){
ans = max(ans, count_two(n,i,j));
}
}
}
// 最后的ans就是所谓的good string的最大长度,n-ans就是应该去掉的部分的长度
cout<<(n-ans)<<endl;
}
int main(int argc, const char** argv) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin>>t;
while(t--){
string s;
cin>>s;
for(auto i = 0; i < s.size(); i++){
buf[i] = s[i]-'0';
}
solve(s.size());
}
return 0;
}
通过count_one
计算从字符串可以提取出 x x x ⋯ x xxx\cdots x xxx⋯x这种Good string
的长度,然后取其中长度的最大值;通过count_two
计算出从字符串中提取出 x y x y ⋯ x y xyxy\cdots xy xyxy⋯xy这种Good String
的字符串的长度,在扫描原来字符串是,用ans
记录这个拟Good String
的长度(如果最后的ans
是奇数要减一,对应末尾多一个x
的情况,末尾不会多余y
,因为y
一定是和x
配对的.在第二种情况下,要关注前面一个字符的类型,比如如果前面统计的是x
,那么只有遇到y
才会扩大Good String
的长度,同时更改关注的字符为y
,这样当下一轮遇到x
时才统计扩大Good string
的长度,这里面是通过ans
的奇偶性来实现的,当ans
是偶数时表示期待下一个字符是x
,当ans
是奇数时表示期待下一个字符是y
.扫描完毕后,有可能找到的拟Good String
末尾多余一个x
,此时需要丢弃这个x
得到的才是Good String
.枚举所有的数字字符可能的组合类型(两个数字字符此时不能相同),从中计算得到的Good String
的最大值,和上面第一种情况的最大值再去最大值,得到的就是可以得到的最长的Good String
的长度.用字符串总长度减去这个长度得到的就是要删减的字符的数量.