2019牛客网暑期多校赛第七场A题--String

本文探讨了如何将01字符串分割成最少数量的完美子串,即每个子串在其所有循环旋转中字典序最小。通过分析和优化算法,避免了暴力解法的超时问题,最终实现了高效解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接:https://ac.nowcoder.com/acm/contest/887/A
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
A string is perfect if it has the smallest lexicographical ordering among its cyclic rotations.
For example: “0101” is perfect as it is the smallest string among (“0101”, “1010”, “0101”, “1010”).

Given a 01 string, you need to split it into the least parts and all parts are perfect.
输入描述:

The first line of the input gives the number of test cases, T (T≤300)T\ (T \leq 300)T (T≤300). test cases follow.

For each test case, the only line contains one non-empty 01 string. The length of string is not exceed 200.

输出描述:

For each test case, output one string separated by a space.

示例1
输入

4
0
0001
0010
111011110

输出

0
0001
001 0
111 01111 0

题意:题目定义了一种01字符串s,要求s是所有s首尾循环(把首字符放到尾部)得到的所有字符串中字典序最小的那个,如果不是,就对字符串进行分段,使得分段的数目最小,然后分成的每段子串都符合要求,比如样例0001,所有循环得到的字符串为:0001,0010,0100,1000。其中字典序最小的为0001也就是本身所以直接输出,样例0010就分成了001和0两个部分。

题解:题目意思很简单,但是不好找,一开始我是先写个暴力的算法来作为测试代码,暴力的写法就是每次从字符串s一个确定的位置依次遍历,找出最长的满足题目要求的部分子串,也就是双重循环,字符串总长度200,平方就是40000,再加上t和其中一些判断的处理,绝对超时,但是我考虑到数据长度为200就不可能是那种想想就得出答案的极端思维题,于是用Python生成了几百组数据用来看规律,果然可以确定的是,除了全是0或者1的特殊字符串,其他的字符串如果要分段,分成的每部分满足题目要求的子串开头一定是0,末尾一定是1(字符串0101100分段,最后一段为00开头和结尾都为0,这个把前面的字符串分好就行),所以定义一个动态数组用来储存10这种关节部分1的位置,然后只针对这些1的位置再进行二重循环遍历,就大大减少了循环次数,提高了效率。

AC代码

#include<bits/stdc++.h>
using namespace std;
bool cmp(string s1,string s2)
{
    return s1<s2;
}
vector<string>q;
//用来判断子串是否满足题目要求
bool check(string s)
{
    q.clear();
    string t;
    t=s+s;
    for(int i=0;i<s.length();i++)
    {
        q.push_back(t.substr(i,s.length()));
    }
    sort(q.begin(),q.end(),cmp);
    if(q[0]==s)
    return true;
    return false;
}
//定义动态数组用来记录10部分1的位置
vector<int>pos;
int main()
{
    int t;
 //   freopen("data.txt","r",stdin);// 按顺序读入(仅此一行)
    cin>>t;
    string s;
    while(t--)
    {
    //  cout<<t<<". ";
        cin>>s;
        pos.clear();
        pos.push_back(-1);
        //按要求把1的位置存入数组
        for(int i=0;i<s.length();i++)
        if(s[i]=='1'&&s[i+1]!='1')
        pos.push_back(i);
        int i=0,j,st=0;
        //开始二重循环遍历
        while(i<s.length())
        {
        //从尾部开始找,如果满足题意就弹出,保证时间效率和答案正确
            for(j=pos.size()-1;j>=0;j--)
            {
                if(pos[j]<i)
                {
                    i++;
                    break;
                }
                int len=pos[j]-i+1;
                //判断子串是否满足题意
                if(check(s.substr(i,len)))
                {
                    cout<<s.substr(i,len)<<" ";
                    i=pos[j]+1;
                    st=i;
                    break;
                }
            }
             
        }
        //这是针对末尾为0的字符串的判断输出,因为我是用1进行判断,如果末尾为0就没法检测到,做特殊判断
        for(int k=st+1;k<=i;k++)
        printf("%c",s[k-1]);
        puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值