CF ECR92 C. Good String

CF ECR92 C. Good String

CF ECR92 C. Good String_第1张图片

题目链接

CF ECR92 C. Good String

题目概述

题目中定义了字符串 a 1 a 2 … a n a_1a_2\dots a_n a1a2an的左旋转 a 2 a 3 … a n a 1 a_2a_3\dots a_na_1 a2a3ana1和右旋转 a n a 1 a 2 … a n − 1 a_na_1a_2\dots a_{n-1} ana1a2an1,然后定义了一个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} a2a3ana1=ana1a2an1,现在给定一个字符串,计算这个字符串经过删减后可以得到一个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 1t1000,2s2105

思路

根据上面那个Good String的定义,可以得到对于一个Good String b 1 b 2 ⋯ b k b_1b_2\cdots b_k b1b2bk,只可能存在以下两种情况:

  1. b 1 = b 2 = ⋯ = b k b_1=b_2=\cdots=b_k b1=b2==bk
  2. b 1 = b 3 = ⋯ = b k − 1 , b 2 = b 3 = b 4 ⋯ = b k , b 1 ! = b 2 , k % 2 = 0 b_1=b_3=\cdots=b_{k-1},b_2=b_3=b_4\cdots=b_k, b_1 != b_2,k\%2=0 b1=b3==bk1,b2=b3=b4=bk,b1!=b2,k%2=0
    从上面的定义代换可以得到,这里简单说一说为什么 k % 2 = 0 k\%2=0 k%2=0时奇偶位上的数字都相同才是Good String,对于一个Good String x y x y ⋯ x y xyxy\cdots xy xyxyxy来说,左旋转之后得到的是 y x y ⋯ x y x yxy\cdots xyx yxyxyx,有旋转之后得到是 y x y x y ⋯ x yxyxy\cdots x yxyxyx旋转实际上是把原来奇偶位上的数字进行了一个简单的变换,原来奇数位的数字变成偶数位的,偶数位的变成奇数位的.如果不满足 k k%2=0 k的话, x y x y ⋯ x y x xyxy\cdots xyx xyxyxyx,左旋转之后是 y x y ⋯ x y x x yxy\cdots xyxx yxyxyxx,二右旋转则是 x x y x y ⋯ x y xxyxy\cdots xy xxyxyxy,这样的两个字符串就不相等了.

所以可以先从 a 1 a 2 ⋯ a n a_1a_2\cdots a_n a1a2an中计算满足上面任意一个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 xxxx这种Good string的长度,然后取其中长度的最大值;通过count_two计算出从字符串中提取出 x y x y ⋯ x y xyxy\cdots xy xyxyxy这种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的长度.用字符串总长度减去这个长度得到的就是要删减的字符的数量.

其它

你可能感兴趣的:(字符串,双指针,暴力)