codeforces MemSQL start[c]up Round 2 - online version A(模拟/二分查找) and B (最长公共子系列)

A题:

题目链接: http://codeforces.com/contest/335/problem/A

题意: 给一个串s 和整数n ,问串s最少可以用多少个长度为n的另一种串ans(每个串都相同)组合成.

      输出最小数目和可能的那种串, 不可能则输出-1.

分析:

      算法一(模拟):

      Ⅰ,当s中的字母种数大于n, 则不可能.

   Ⅱ,在s中出现次数多的字母,在ans中也应该多. ⑴可以先将s中出现过的字母都加一个到ans中.

         ⑵若此时结果串的长度小于n, 则找到需要当ans串最多的那个字母, 将其加一个到ans中.

         ⑶依此重复⑵,直到长度达到n.


      算法二(二分查找):

      这是看到tourist 思路写的,果然巧妙........

      以需要长度为n的串ans的个数进行二分查找, 每次求出当假设有mid个串时,这个串需要的最小长度,

      如果大于n, 说明mid个肯定不够, 得往后一半找, 反之往前一半找. 


代码: (模拟)

#include<iostream>
#include<string>
using namespace std;
int a[28],b[28];
int main(){
    string s,f; 
    int n,m=0; 
    cin>>s>>n;
    int len=s.size();
    for(int i=0;i<len;++i)
        ++a[s[i]-'a'];
    
    for(int i=0;i<26;++i)
        if(a[i]) f+=char(i+'a'),++m,++b[i];
    if(m>n) {
        cout<<-1<<endl;
        return 0;
    }
    for(int i=1,mj;i<=n-m;++i){
        double Max=0;
        for(int j=0;j<26;++j)
            if(a[j]){
                double k=(double)a[j]/b[j];
                if(k>Max) Max=k, mj=j;
            }
        f+=char(mj+'a');
        b[mj]++;
    }
    int ans=1;
    for(int i=0;i<26;++i)
        if(a[i]){
            ans=max(ans,a[i]/b[i]+(a[i]%b[i]?1:0));
        }
    cout<<ans<<endl;
    cout<<f<<endl;
    return 0;
}



代码: (二分)

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int num[28];
int main(){
    string s;
    int n;  cin>>s>>n;
    int len=s.size();
    for(int i=0;i<len;++i) num[s[i]-'a']++;
    int l=1, r=len+1;   /// 结果值的两个极限范围
    while(l<r){
        int mid=(l+r)>>1;
        int need=0;  
        for(int i=0;i<26;++i)
            need+=(num[i]+mid-1)/mid; /// 防止num[i]不是整除mid时要另外加1
        if(need>n) l=mid+1;
        else r=mid;
    }
    if(l==len+1){
        cout<<-1<<endl; return 0;
    } 
    int ans=l, tot=0;
    cout<<ans<<endl;
    
    for(int i=0;i<26;++i){
        int k=(num[i]+ans-1)/ans;
        tot+=k;
        while(k--)
            cout<<char(i+'a');
    }
    n-=tot;
    while(n--) cout<<'a';
    cout<<endl;
    return 0;
}




B题:

题目链接:  http://codeforces.com/contest/335/problem/B

分析: 第一眼看上去串的长度为5*10^4, 冒似只能用O(n)的算法可解. 而这样的算法从来没见过......

      其实不然, 注意一个条件"如果有存在长度为100的回文子串则输出长度为100的,否则输出最长的",

      可以发现: 一个长度大于100的回文串都可以变成长度为100的

      如: 长度为101的只要删除中间的一个就变为长度为100的,

          长度为102的删除中间两个也变为长度为100的......

      意思就是, 得到的结果串的长度一定小于等于100.

      又因为原串中全为小写字母, 所以最多出现26个字母,

     还可以发现, 当长度达到2600时, 肯定会出现长度为100回文子串, 因为肯定会有一个字母出现次数大于等于100

      也就是说, 最终, 我们最后取2600个字母, 那么可以用O(n^2)(这里n<=2600) 算法...........

代码:

#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
const int maxn=2601;
int dp[maxn][maxn]; ///dp[i][j]表示区间[i,j]中回文子串的长度
int L[maxn][maxn];
int R[maxn][maxn];
int main(){
    
    string s; cin>>s;
    int len=min(maxn,(int)s.size());
    for(int i=0;i<len;++i)  
        dp[i][i]=1;
    for(int i=1; i<len; ++i)
        for(int j=0; j+i<len; ++j) {
            int k=j+i;
            if(dp[j+1][k]>dp[j][k]) {
                dp[j][k]=dp[j+1][k];
                L[j][k]=j+1;
                R[j][k]=k;
            }
            if(dp[j][k-1]>dp[j][k]) {
                dp[j][k]=dp[j][k-1];
                L[j][k]=j;
                R[j][k]=k-1;
            }
            if(s[j]==s[k]&&dp[j+1][k-1]+2>dp[j][k]) {
                dp[j][k]=dp[j+1][k-1]+2;
                L[j][k]=j+1;
                R[j][k]=k-1;
            }
        }
    int ans=min(dp[0][len-1],100);
    char ch[102]="";
    int ls=0, rs=len-1;
    int lc=0, rc=ans-1;
    while(lc<=rc){ 
        if(lc==rc){
            ch[lc]=s[ls]; break;
        }
        if(s[ls]==s[rs]){
            ch[lc]=ch[rc]=s[ls];
            lc++; rc--; 
            ls++; rs--; 
            continue;
        }
        int lx=ls, rx=rs;
        ls=L[lx][rx];
        rs=R[lx][rx];
    }
    cout<<ch<<endl;
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值