字符串刷题(day1)题解

字符串刷题

vjudge题单

一、Erase First or Second Letter

[Erase First or Second Letter](Erase First or Second Letter - CodeForces 1917B - Virtual Judge (vjudge.net))

每个字符串可以看做由一个前缀字符和一个后缀字符串组合而成,不同长度的后缀字符串对答案的贡献是相同的,但是前面的字符是不同的,所以只用记录前面出现字符的种类即可。每次都加上前面的字符种类。

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;

int main(){
    int t; cin>>t;
    while(t--){
        int n; cin>>n;
        string s; cin>>s;
        int ans=0,cnt=0;
        map<char,int> mp;
        for(int i=0;i<s.size();i++){
            ans+=cnt;
            if(mp[s[i]]==0) cnt++;
            mp[s[i]]++;
        }
        ans+=cnt; //后缀为空
        cout<<ans<<'\n';
    }
}

二、Swap and Reverse

[Swap and Reverse](Swap and Reverse - CodeForces 1864B - Virtual Judge (vjudge.net))

第一种操作只能奇数偶数下标的字符翻转,而如果第二种操作的k为偶数,那么可以做到奇偶下标字符的相互转换。若为奇数,则仍然只能奇数偶数下标内部翻转。

#include<iostream>
#include<algorithm>
using namespace std;
const int M=1e5+5;
char ch1[M],ch2[M];
int main(){
     int t; cin>>t;
     while(t--){
          int n,k; cin>>n>>k;
          string s; cin>>s;
          //s=" "+s;
          if(k%2==0){
               sort(s.begin(),s.end());
               cout<<s<<endl;
               continue;
          }
          s=" "+s;
          int cnt1=-1,cnt2=-1;
          for(int i=1;i<=n;i++){
              if(i%2){
                    ch1[++cnt1]=s[i];
              }
              else{
                     ch2[++cnt2]=s[i];
              }
          }
          sort(ch1,ch1+cnt1+1);
          sort(ch2,ch2+cnt2+1);
          int j=-1; int p=-1;
          for(int i=1;i<=n;i++){
               if(i%2){
                    cout<<ch1[++j];
               }
               else{
                    cout<<ch2[++p];
               }
          }
          cout<<endl;

     }
}

三、Largest Subsequence

[Largest Subsequence](Largest Subsequence - CodeForces 1905C - Virtual Judge (vjudge.net))

每次选取字典序最大的子序列,向右循环移动一次,最少多少次移动可以使整个字符串有序。观察得到,只要找到字典序最大的子序列以后,以后的每一次翻转都是基于这个子序列的子序列上面的。最少的反转次数就是字典序最大的字符后面的字符数量(每一次右移会有一个比最大字典序字符小的字符,移动到它的前面,当最大字典序的字符移动到子序列末尾的时候,再次移动就可以视为没有移动)。

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
const int M=2e5+5;
int vis[M];
int main(){
     int t; cin>>t;
     while(t--){
          int n; cin>>n;
          string s; cin>>s;
          string tmp; tmp=s;
          for(int i=0;i<n;i++) vis[i]=0;
          sort(tmp.begin(),tmp.end());
          string ss="";
          char ch=0;
          for(int i=s.size()-1;i>=0;i--){
               if(ch<=s[i]){
                    ch=s[i];
                    ss+=ch;
                    vis[i]=1;
               }
          }
          int ans=-1;
          for(int i=0,t=0;i<n;i++){
               if(vis[i]){
                    if((ch==ss[t])&&ans==-1) ans=t;
                    s[i]=ss[t++];
               }
          }
          if(tmp==s) cout<<ans<<endl;
          else cout<<"-1"<<endl;  
     }
}

四、XOR Palindromes

[XOR Palindromes](XOR Palindromes - CodeForces 1867B - Virtual Judge (vjudge.net))

例:101011

对于1,6两个位置的字符都为1,是对称的,可以选择与1异或变成0,也可以与0异或变成1。每一对相等的对称字符都可以做这两种选择。而对于不相等的对称字符,若想让两者相等,必定会有一位与1异或操作,另一位与0异或,也就是异或字符串中必定会出现一个1。

接下来考虑字符串长度的奇偶性,我们设 d d d为两者不同的字符对的对数,s为相同的字符对的对数。

(1)若为偶数,则一的数量有以下几种情况 d , d + 2 , d + 4 , … … , d + 2 ∗ s d,d+2,d+4,……,d+2*s d,d+2,d+4,……,d+2s

(2)若为奇数,由于中间的数可以随意改变,则一的数量有以下几种情况 d , d + 1 , d + 2 , … … , d + 2 ∗ s + 1 d,d+1,d+2,……,d+2*s+1 d,d+1,d+2,……,d+2s+1

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int M=1e5+5;
int t[M];
int main(){
    int p; cin>>p;
    while(p--){
        int n; cin>>n;
        string s;
        cin>>s;
       // int cnt=0;
        int c[2]={0,0};
        for(int i=0;i<=n;i++) t[i]=0;
        s=" "+s;
       for(int i=1;i<n-i+1;i++){
            if(s[i]==s[n-i+1]) c[1]++;
            else c[0]++;
       }
      // cout<<c[0]<<" "<<c[1]<<endl;
       if(n&1) for(int i=c[0];i<=c[0]+2*c[1]+1;i++) t[i]=1;
       else for(int i=c[0];i<=c[0]+2*c[1];i+=2) t[i]=1;
     //   for(int i=0;i<=n;i++){
     //      cout<<t[i]<<" ";
     //   }
     //  cout<<endl;
       for(int i=0;i<=n;i++){
          putchar(t[i]+'0');
       }
       cout<<endl;
    }
}

五、Strong Password

[Strong Password](Strong Password - CodeForces 1845C - Virtual Judge (vjudge.net))

枚举每一个密码字符,当有一个字符没有在密码数据库中,就输出YES

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;

int main(){
     int t; cin>>t;
     while(t--){
          string s; cin>>s;
          int m; cin>>m;
          string l; cin>>l;
          string r; cin>>r;
          l=" "+l; r=" "+r;
          int pos=0;
          int f=0; int maxp=0;
          for(int i=1;i<=m;i++){
               for(int j=l[i];j<=r[i];j++){
                    int tmp=s.find(j,pos);
                    if(tmp==-1){
                         f=1;
                         break;
                    }
                    ++tmp;
                    if(tmp>maxp){
                         maxp=tmp;
                    }
               }
               pos=maxp;
               if(f==1){
                    break;
               }
          }
          if(f==1){
               cout<<"YES"<<endl;
          }
          else cout<<"NO"<<endl;
     }
}

六、Row Major

[Row Major](Row Major - CodeForces 1844D - Virtual Judge (vjudge.net))

观察得到,行列的值都是n的因数。

举个例子:若n为6,那么可以组成23或3 * 2的网格图,若原字符串为ababab,那么一定在32的网格图中两行之间会相同,abcabc同理。所以不难发现,以n的因数为循环节是不可以的,所以要想使不相同的字符最少,就要以n的最小非因数大小为循环节大小。

#include<iostream>
#include<algorithm>
using namespace std;
const int M=1e6+5;
char ans[M];
int main(){
     int t; cin>>t;
     while(t--){
          int n; cin>>n;
          if(n==1){
               cout<<"a"<<endl;
               continue;
          }
          if(n==2){
               cout<<"ab"<<endl;
               continue;
          }
          int pos=0;
          for(int i=1;i<=n;i++){
               if(n%i){
                    pos=i;
                    break;
               }
          }
          for(int i=0;i<pos;i++){
               ans[i]=i+'a';
          }
          for(int i=0;i<n;i++){
               cout<<ans[i%pos];
          }
          cout<<endl;
     }
}

七、Game with Reversing

[Game with Reversing](Game with Reversing - CodeForces 1834C - Virtual Judge (vjudge.net))

字符串能够在翻转后匹配要么是正向匹配,要么是逆向匹配,所以可以正反处理出需要修改的字符数量。

字符串翻转偶数次后,与原字符串顺序相同,翻转奇数次后,与原字符串顺序相反。

正向匹配一定翻转偶数次,逆向匹配一定翻转奇数次。

#include<iostream>
#include<algorithm>
using namespace std;

int main(){
     int t; cin>>t;
     while(t--){
          int n; cin>>n;
          string s,p; cin>>s>>p;
          int cntz=0;
          for(int i=0;i<n;i++){
               if(s[i]!=p[i]){
                    cntz++;
               }
          }
          int cntf=0; int cnt=0;
          for(int i=n-1;i>=0;i--){
               if(s[i]!=p[cnt++]){
                    cntf++;
               }
          }
          if(cntz==0){
               cout<<0<<endl;
               continue;
          }
          if(cntf==0){
               cout<<2<<endl;
               continue;
          }
          int ans=cntz*2-(cntz%2==1); //若修改次数为奇数,最后一次不用反转
          ans=min(ans,cntf*2-(cntf%2==0)); //若修改次数为偶数次,最后一次不用反转
          cout<<ans<<endl;
     }
}

八、Tear It Apart

[Tear It Apart](Tear It Apart - CodeForces 1821C - Virtual Judge (vjudge.net))

枚举每个字母作为分割边界,我们对分割出来的最长子串进行操作,每次除以2,记录操作次数,即模拟删除过程,最后统计操作次数当中的最小值即可。

#include<iostream>
#include<algorithm>
using namespace std;

int main(){
     int t; cin>>t;
     while(t--){
          string s; cin>>s;
          int ans=1e9+5;
          for(char i='a';i<='z';i++){
               int cnt=0; int maxn=0;
               for(int j=0;j<s.size();j++){
                 //   cout<<char(i+'a')<<endl;
                    if(s[j]!=i){
                         cnt++;
                    }
                    else{
                         cnt=0;
                    }
                    maxn=max(maxn,cnt);
               }
               int res=0;
               while(maxn){
                    ++res;
                    maxn/=2;
               }
               ans=min(ans,res);
          }
          cout<<ans<<endl;
     }
}
  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值