codevs 1065 01字符串 题解报告

博客探讨了Codevs上的01字符串问题,要求字符串长度为n且不能有三个连续相同的字符。博主通过样例解释了问题,并提到只要避免出现"000"或"111"即可。解决方案是使用深度优先搜索(DFS)策略,关注前两个字符的状态来生成满足条件的字符串。

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

0.0
0.0
0.0
题目描述 Description
输出仅有0和1组成的长度为n的字符串,并且其中不能含有3个连续的相同子串。

输入描述 Input Description
输入文件只有一行一个整数n,表示有0和1组成的字符串的长度。0<=n<=30。

输出描述 Output Description
输出文件只有一行一个整数,表示所有满足条件的字符串的个数。

样例输入 Sample Input
1

样例输出 Sample Output
2

数据范围及提示 Data Size & Hint

0.0
0.0
0.0
说实话我 没 看 懂 题!!
问了他们做过的,
才知道。
只要字符串里没有 000 111 就好了。

嗯,
直接dfs
记录上一个和上上个是什么就行。

0.0.0
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
<think>好的,我现在要解决这个问题。题目是说,给定一个长度为n的01串,其中有一些位置是问号,可以填01。我需要填充这些问号,使得最终的字符串中互重叠的0011子串的数量最多。然后输出这个最大的数量。比如样例输入1110?0,填0得到111000,其中有一个00一个11,但输出是2,可能样例中的正确填法可能是填1?或者是是我理解错了?或者可能样例的输出是2,比如填0得到两个11的情况? 首先,我得仔细看样例。原输入1110?0。样例输出是2。提示说填0的话得到111000,里面有一个00一个11,但输出是2?可能我的理解有问题。或者可能提示中的说法是说填0的话会有两个可能的子串?或者可能我的理解方式对。或者可能这个样例的正确填充方式是填1,比如变成111010,这时候可能有两个互重叠的子串?比如11(前两位)11(第二位到第位),但这样的话会有重叠,所以行。或者可能正确的填法是让两个子串重叠。比如,假设填充问号为0,得到111000。这时候,可能的子串是111中的前两个1构成11,中间的两个1构成另一个11?但是这两个11是重叠的,所以只能算其中一个。或者可能填充后的字符串1100各出现一次,总共有两个?比如,例如,原题中的样例输出是2,说明这个情况。例如,111000中有个连续的1,可以拆分成两个11吗?比如第一个第二个字符是11,第第四个是10,这样可能行。或者可能这个样例中的正确填法其实是填0得到10,那么其中的互重叠的1100的数量是1+1=2? 这个时候,我需要先理清楚题目中的子串如何计算。题目中的互重叠的0011子串,也就是说,每个子串必须是两个连续的相同字符,并且这些子串之间能有重叠的部分。例如,在字符串111中,可以有11(前两位)后面的两位(第二、第位),但是这两个子串是重叠的,所以只能算其中一个。或者可能是否可以拆分多个,比如如何选择才能得到最多的重叠的子串? 例如,字符串111中可以有两个重叠的11吗?比如前两位是第一个子串,第位剩下的无法组成,所以只能是两个?或者可能行,因为前两位用了之后,剩下的第位无法再组成一个子串。比如,假设字符串111,那么可能的子串是11(位置0-11无法使用,或者位置1-2的11,但这样两个子串是重叠的。所以只能选一个。 那在样例输入1110?0中,当填充问号为0时,得到字符串111000。这时候可能的子串情况: - 前面的1,可以选两个11:比如位置0-111,然后位置2-3的10行),或者位置1-2的11?但是这两个11子串是相邻的,是重叠的。例如,假设选0-111,那么接下来的子串可以从位置2开始。那位置2是1,后面是0,所以无法组成。或者第个字符是1,后面是0,所以无法。因此,前面的1只能组成一个11子串。然后后面是0,可以组成两个00子串吗?比如位置3-4的00,位置4-5的00。这两个是重叠的吗?是的,但它们的长度是两个,所以这两个子串是相邻的,是否允许这样? 比如,假设字符串0000,那么可以分成000-100(2-3),这两个重叠,所以总共有两个。这样的话,在样例中,当填充为111000时,前面的1可以组成一个11,后面的0可以组成两个00。总共有个子串?但是样例的输出是2。这时候说明我的理解可能有错误。 或者,可能题目中的每个子串必须是两个连续的字符,并且每个字符只能属于一个子串。例如,当有个连续的0时,最多只能有一个00子串吗?比如,取前两位,那么第位单独剩下,无法组成一个。或者可以拆分为0-11-2?但这样这两个子串是重叠的,所以允许。或者,题目中的子串必须是互重叠的,即每个字符最多属于一个子串。因此,对于个连续的0,可以有两个00子串吗?例如,第一个第二个0组成一个子串,第0无法单独使用。或者可能行,因为个连续的0只能组成一个长度为2的子串,剩下的第个无法组成。或者是否允许相邻的两个?比如,0-100,然后2-3也是00?比如在四个0的情况下,可以分成前两位后两位,这样是两个重叠的子串。例如,0000可以分成0-12-3,所以是两个。这时候,当有个连续的0,比如000,只能取一个00子串,然后第0无法使用。所以在这种情况下,个连续的0只能有一个00子串。这样,在样例中,当填充为111000时,前面1可以组成一个11(比如0-1),剩下的第1无法组成一个。后面的0只能组成一个00(比如3-4),剩下的第五个0无法组成。所以总共有两个子串。这样,样例的输出是2,符合要求。所以这说明,在计算时,对于连续的多个相同字符,可以分割成多个重叠的两位子串。例如,四个连续的0可以分割成两个重叠的00子串,每个占据两位。而个连续的0只能分割成一个00子串,剩下的一个0无法使用。 那么,问题转化为如何填充问号,使得将整个字符串分割成尽可能多的重叠的两位的0011子串。 现在,问题转化为,如何填充问号,使得这样的子串数量最大。这是一个典型的动态规划问题,或者贪心问题。因为对于每个位置,我们需要决定是否与前面的字符形成一个有效的子串,或者保留到后面的位置。 可能的贪心策略是尽可能多地在每个可能的位置形成子串。例如,从前往后扫描,每当可以形成0011子串时,就立即取这个子串,并跳过下一个字符。这样可以确保尽可能多的子串被选取。但是,是否这种贪心策略是正确的?或者是否存在某些情况需要延迟选择以获得更大的总数? 例如,考虑字符串0000。贪心策略会先取前两位的00,然后后两位的00,这样得到两个,是正确的。而对于另一种情况,例如01000。假设问号填充的情况是01000。那么,如果按照贪心的策略,第一个字符0第二位1无法组成子串,第二位10也无法,第0第四位0可以组成00,然后第五位0无法组成,所以总共有1。但是如果第取,而是第四第五位组成00,那么可能得到1。所以结果一样。或者可能存在更复杂的情况? 或者例如,字符串000。贪心策略取前两位的00,剩下的第位无法,总数1。而另一种分割方式无法更好,所以正确。 另一个例子,字符串00000。贪心策略取0-1,跳过2,取3-4,总共有两个。而整个字符串五个0,可以被分割为0-1, 2-3,剩下一个,无法。所以总共有两个。或者是否存在更好的分割方式?比如,分割为1-23-4,这样也是两个。所以无论怎样分割,只能得到两个子串。因此,贪心策略正确。 那么,贪心策略是否适用于本题? 可能的贪心策略是:从左到右扫描字符串,每次尽可能取当前位置一个位置组成一个有效的子串(即0011)。如果这两个字符中有一个是问号,则填充相应的值使得这两个字符相同。如果这两个字符已经是相同的,或者可以通过填充问号得到相同,那么取这个子串,并跳过后面的一个字符。否则,就跳过当前字符,继续处理下一个。 这样的策略是否能够最大化子串的数量? 例如,对于样例输入1110?0,原始字符串1 1 1 0 ? 0。当处理到前两位11,可以组成11,计数加1,然后跳过第位。接下来处理第四位的0第五位的?。此时,第五位是?,可以填0或者1。假设填0的话,这两个位置是00,形成00,计数加1,然后跳过第六位。这样总共有2,符合样例的输出。或者可能处理方式同? 那按照这个策略,当处理到前两位的11组成11,计数加1,然后处理第位的1第四位的0?这时候可能第位已经被跳过? 或者,贪心策略的处理方式可能是在处理到i的位置时,尝试将ii+1的位置组成子串。如果可以的话,则计数加1并且i增加2;否则,i增加1。例如,这样: i从0开始: 当i=0,查看ii+1的位置是否可以组成0011。例如,原字符串中的字符是否可以通过填充问号形成相同的两个字符。如果可以的话,填充这两个位置的问号为相同的值,并计数加1,然后i +=2。否则,i +=1。 这个处理方式的正确性如何? 比如,对于字符串0?0。原字符串个字符是0、?、0。i=0时,检查ii+1的位置:0?。可以填充?为0,形成00,计数加1,然后i跳到2。此时,i=2,已经到末尾,结束。总共有1个子串。如果处理方式是填充问号为0,而是后面的?0填充为0,那么是否有更好的结果?例如,原字符串0?0,如果i=0的时候处理0?,填充为0,得到00,然后剩下的0单独,所以总共有1。或者i=0的时候处理,而处理i=1i=2的位置?,此时i=1的位置是?,i+10。可以填充?为0,形成00,计数加1。此时,总共有1。所以无论哪种方式,总数一样。所以贪心策略正确。 另一个例子,字符串0?1。i=0时,0?,可以填充?为0组成00,计数加1。剩下的1无法处理,总数1。或者,如果填充?为1,则01无法组成,但?1可以组成11吗?原字符串0 ? 1。如果i=0,无法组成0011,所以i增加到1。此时,i=1i=2是?1。填充?为1组成11,计数加1。总共有1。所以两种方式结果一样。所以贪心策略会得到正确的答案。 这说明,贪心策略可能有效。那么,现在的问题转化为,如何实现这个贪心策略,同时处理问号的情况。 具体来说,当处理到i位置时,需要检查ii+1的位置是否可以组成0011。如果有问号,则允许填充这两个位置的问号为相同的值。然后,如果确实可以组成这样的子串,则计数加1,并跳过i+1的位置。否则,处理,继续下一个位置。 那么,如何判断ii+1的位置是否可以组成0011? 对于这两个字符来说,可能有以下几种情况: 1. 两个都是确定的字符(01)。如果相同,则可以组成子串。否则,能。 2. 其中一个或两个是问号。此时,需要判断是否可以填充问号使得两个字符相同。 例如: a. i是0,i+1是?:可以将i+1的?填为0组成00。 b. i是?,i+11:可以将i填为1组成11。 c. ii+1都是?:可以都填为01组成0011。 在这种情况下,只要存在至少一个字符是01,或者两个都是问号,那么就可以填充形成相同的字符。 综上,只要这两个位置中至少有一个字符是确定的,且它们可能被填充为相同的,或者两个都是问号,那么就可以形成子串。 所以,当处理ii+1的位置时,只要这两个字符可以被填充为相同,那么就可以形成一个子串。 所以,对于每个i,从0到n-2: 如果ii+1的位置可以组成相同的字符(即两个字符中有至少一个确定的值,或者两个都是?),则取这两个字符作为一个子串,填充问号使得它们相同,然后i +=2。 否则,i +=1。 这样处理下去,得到的子串数量是否是最大的? 假设这样的贪心策略是有效的,那么如何实现? 现在,我需要用C语言编写一个程序,按照这个贪心策略处理字符串,填充问号,并统计最多子串数目。 需要注意,因为字符串长度可能达到1e6,所以算法必须是O(n)的时间复杂度。 现在,具体的实现步骤: 初始化计数器ans=0,i=0。 循环,当i < n-1: 检查ii+1的位置是否可以组成相同的字符: - 如果s[i]s[i+1]都是确定的字符,并且相等,则ans++,i+=2。 - 否则,如果至少有一个字符是问号,并且可以通过填充使得两者相同: 例如: 情况1:s[i]是0,s[i+1]是?。填充?为0,形成00。ans++,i +=2. 情况2:s[i]是?,s[i+1]是1。填充?为1,形成11.ans++,i +=2. 情况3:s[i]s[i+1]都是?。可以填为01。比如,填为0,形成00。ans++,i +=2. 如果上述情况满足,则ans++,i +=2. 否则,无法形成子串,i +=1. 这样处理是否正确? 例如,对于样例输入字符串1110?0。索引0~5: 0:1, 1:111,ans+1=1,i=2. i=2,此时查看s[2]=1,s[3]=0相同,无法组成。所以无法处理。i +=1 → i=3. i=3,s[3]=0,s[4]=? → 可以填?为0组成00。ans=2,i=5. i=5,已经到最后一个位置(n-1=5),无法处理,结束。总共有2,符合样例。 另一个例子:原字符串0?0的处理: i=0,s[0]=0,s[1]=? → 可以填为0,ans=1,i=2. s[2]=0,此时i=2,无法处理i+1=3,结束。总共有1. 另一个例子:字符串???的处理: i=0,s[0]s[1]都是? → 可以填为0组成00。ans=1,i=2. 此时i=2,是否还有i+1=3?假设字符串长度是3,则i=2时无法处理。总共有1。而整个字符串可以分成个?,填充成000,那么可以形成两个00?例如,前两位00,然后两位00(i=01,然后i=2无法处理)。或者贪心处理的话,只能得到一个。这似乎是最优解。例如,填充为0000的话,可以形成两个00子串。比如,假设字符串长度是4,那么处理i=01得到00,i=23得到00,总共有2。而如果字符串长度是3,填充为000,则只能有一个00子串,无法得到两个。所以贪心策略在这种情况下是正确的? 这可能说明,贪心策略无法处理某些情况。例如,当处理i=0i=1的??时,形成00,但剩下的i=2的?无法组成。而如果选择i=0的?i=1的?填为00,或者填为11,那么后面无法组成。而另一种情况,如果处理i=0的时候跳过,然后在i=1i=2的??填为00,这样可以得到一个子串。总数一样。 所以,贪心策略在这种情况下正确。 可能,贪心策略是正确的,因为它总是尽可能多地选择子串,从而最大化总数。 现在,我需要将这个逻辑转化为代码。 具体来说,在代码中如何处理每个i的位置: 对于每个i,从0开始: 如果i >= n-1,break. 检查s[i]s[i+1]是否可以形成相同的字符: 条件: (s[i] == s[i+1]) 或者 (s[i] == '?' 或 s[i+1] == '?') 或者 两个都是 '?'. 因为,如果两个字符中有至少一个是问号,那么可以填成相同的字符。例如,如果一个0,另一个是?,则填?为0;如果一个1,另一个是?,填为1;如果两个都是?,填为01都行。所以,只要两个字符中可以变成相同,就可以形成一个子串。 所以,只要s[i]s[i+1]中有至少一个是确定的同字符,就可以组成子串。例如,如果s[i]是0,s[i+1]是1,则无法;如果s[i]是0,s[i+1]是?,则可以。 所以,条件判断是:s[i] == s[i+1] 或者其中至少一个是问号。但是,这可能完全正确。例如,当s[i]是0,s[i+1]是1时,它们同,且都是问号,所以无法组成子串。因此,只有当s[i] s[i+1]中至少有一个是问号,或者它们本身相同的时候,才可以组成子串。 因此,在代码中,当处理ii+1的位置时: if (s[i] == s[i+1] || s[i] == '?' || s[i+1] == '?'): ans +=1 i +=2 else: i +=1 但是,这可能有问题。例如,当s[i]是0,s[i+1]是1,那么无法组成相同的,所以必须跳过。只有当至少有一个是问号,或者他们本身相同的时候,才能组成子串。 例如,当s[i]s[i+1]中有至少一个问号,或者它们本身相同,则可以组成。否则行。 所以,条件判断应写为: if (s[i] == s[i+1] || (s[i] == '?' || s[i+1] == '?')) 在这种情况下,即可形成子串。 例如,当s[i]是0,s[i+1]是? →可以填充?为0,形成00。满足条件。 当s[i]是0,s[i+1]是1满足条件,无法形成。 当s[i]s[i+1]都是? →可以填为相同,形成0011,满足条件。 所以,这样判断是正确的。 因此,代码的大体结构是: 初始化ans=0,i=0。 循环,当i < len-1: 检查当前ii+1的位置是否满足条件: if (s[i] == s[i+1] || s[i] == '?' || s[i+1] == '?'): ans +=1 i +=2 else: i +=1 这样处理后,ans即为所求。 但是,是否正确?例如,对于样例输入1110?0: 原字符串是 '1','1','1','0','?','0' i=0: s[0]s[1]都是1 →满足条件,ans=1,i=2. i=2: s[2]是1,s[3]是0满足条件,所以i +=1 →i=3. i=3: s[3]是0,s[4]是? →满足条件,ans=2,i=5. i=5 →超过n-2(假设n=6,索引5是最后一个字符,i=5时无法处理i+1=6),循环结束。总ans=2,正确。 另一个测试用例,输入0000: 处理i=0: s[0]s[1]都是0 →ans=1,i=2. i=2,s[2]s[3]都是0 →ans=2,i=4。循环结束。正确。 另一个测试用例,输入0?0: i=0: s[0]是0,s[1]是? →条件满足,ans=1,i=2. i=2,无法处理。总ans=1,正确。 另一个测试用例,输入0?1: i=0: s[0]是0,s[1]是? →条件满足,ans=1,i=2. i=2,无法处理。总ans=1. 但是,如果填充?为1的话,后面的?1可以组成11吗?例如,原字符串0 ? 1。按照上述处理方式,i=0时,取0?,填充为0,得到00。此时i=2是1,无法处理。所以总数1。而如果i=0处理,而是处理i=1i=2的位置?1,填充?为1,得到11。此时总数1。所以两种方式结果相同,所以贪心策略是正确的。 那,这样看来,这个贪心策略是正确的。因此,代码可以按照这个逻辑编写。 接下来,代码的实现。由于输入字符串长度可能很大(1e6),需要用C语言高效处理。 步骤: 读取输入字符串:可以用fgets或者逐字符读取,注意去掉可能的换行符。 遍历字符串,按照上述贪心逻辑处理。 例如,代码的大致结构: #include <stdio.h> #include <string.h> int main() { char s[1000001]; scanf("%s", s); int n = strlen(s); int ans = 0; for (int i = 0; i < n - 1; ) { if (s[i] == s[i+1] || s[i] == '?' || s[i+1] == '?') { ans++; i += 2; } else { i++; } } printf("%d\n", ans); return 0; } 这样是否正确? 例如,样例输入1110?0字符串各字符:索引0:1, 1:1 →条件满足,ans=1,i=2. i=2时,字符是10 →条件满足,i增加1到3. i=3时,字符是0? →条件满足,ans=2,i=5. 循环结束,输出2。正确。 另一个测试用例输入0000: i=0i=10 →条件满足,ans=1,i=2. i=2i+1=3 →00 →条件满足,ans=2,i=4。循环结束,输出2. 输入0?0: i=0i+10? →条件满足,ans=1,i=2。循环结束,输出1. 输入0?1: i=0i+10? →条件满足,ans=1,i=2。循环结束,输出1. 输入??? →个问号: i=0i+1是?? →条件满足,ans=1,i=2。i=2时,i+1是3,超过长度。所以输出1. 这可能是最优的,例如,如果个问号填充为000,可以得到两个子串吗?例如,i=0i+100 →ans=1,i=2。无法继续处理。所以总共有1。而如果填充为 00 0,那么只能一个。所以贪心算法正确。 但是如果字符串是四个问号,例如????: i=01 →ans=1,i=2. i=23 →条件满足,ans=2,i=4。循环结束。输出2。这正确,因为填充为0000,可以得到两个子串。 那这说明代码是正确的? 综上,这个贪心算法是正确的。因为每次尽可能地在当前位置取一个子串,从而保证全局最优。 因此,最终的答案就是这个贪心算法的实现。</think>要解决这个问题,我们需要填充字符串中的问号(`?`),使得互重叠的 `00` `11` 子串数量最多。通过贪心策略,我们可以高效地实现这一目标。 ### 方法思路 **贪心策略**:从前往后遍历字符串,尽可能在每个位置形成有效的子串(`00` 或 `11`)。具体步骤如下: 1. **判断条件**:当前位置 `i` `i+1` 的字符可以相同(包括两者相同、至少有一个是问号的情况)。 2. **填充处理**:若满足条件,则视为形成一个有效子串,计数器加1,并跳过下一个字符;否则继续处理下一个位置。 这种方法确保每次尽可能多地形成子串,从而最大化总数。 ### 解决代码 ```c #include <stdio.h> #include <string.h> int main() { char s[1000001]; scanf("%s", s); int n = strlen(s); int ans = 0; for (int i = 0; i < n - 1;) { if (s[i] == s[i + 1] || s[i] == '?' || s[i + 1] == '?') { ans++; i += 2; } else { i++; } } printf("%d\n", ans); return 0; } ``` ### 代码解释 1. **输入读取**:读取输入字符串并计算其长度。 2. **遍历处理**:使用循环遍历字符串,检查每对相邻字符是否符合形成有效子串的条件。 3. **计数与跳过**:当条件满足时,计数器增加,并跳过下一个字符;否则继续处理下一个字符。 4. **结果输出**:最终输出最大子串数量。 该方法的时间复杂度为 O(n),适用于处理大规模输入,确保高效运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值