有三个字符串 S,S1,S2,其中,S 长度不超过 300,S1 和 S2 的长度不超过 10。
现在,我们想要检测 S1 和 S2 是否同时在 SS 中出现,且 S1 位于 S2 的左边,并在 S 中互不交叉(即,S1的右边界点在 S2 的左边界点的左侧)。
计算满足上述条件的最大跨距(即,最大间隔距离:最右边的 S2 的起始点与最左边的 S1 的终止点之间的字符数目)。
如果没有满足条件的 S1,S2 存在,则输出 −1。
例如,S= abcd123ab888efghij45ef67kl
, S1= ab
, S2= ef
,其中,S1 在 S中出现了 2 次,S2 也在 S 中出现了 2 次,最大跨距为:18。
输入共一行,包含三个字符串 S,S1,S2,字符串之间用逗号隔开。
数据保证三个字符串中不含空格和逗号。
输出一个整数,表示最大跨距。
如果没有满足条件的 S1 和 S2 存在,则输出 −1。
abcd123ab888efghij45ef67kl,ab,ef
18
1、用for循环写的
//用for循环写的代码及其注释
/*字符串跨距解题思路:先输入s,s1,s2,这三个字符串,然后再分别确定左边界与右边界的位置,确定了以后,再
用公式把距离给计算出来。注意,这题与之前的题有所不同,它需要先在纸上计算出来龙去脉,要举例分析,要画图观察,
那个最后的计算式子更是通过计算和观察总结出来的结果。不像以前那些题一样可以直接上手写,至少这题不行。
*/
#include
#include
#include
using namespace std;
int main()
{
string s, s1, s2;//先读入三个字符串是一切的基础。
char c;
/*注意,这里定义的是一个字符c,之所以这样定义,是因为题上输入字符串有","相连,就和往常的啥也没有的
输入字符串截然不同。说实话,刚开始看到这样的输入方式,我压根不知道怎么输入。
这里定义字符c,是把输入的每个字符串都分解为字符,所以它才会用while循环输入。
*/
while (cin >> c, c != ',')
s += c;
/*这个while循环的意思是,输入字符c,可以一直输入,直到输入","就停止输入,多个的字符拼接起来
就成了一个字符串,就成了题目中所要求的输入字符串s,s1,s2了。那个s+=c就是将多个字符不断连接,使其成为
一个字符串的关键。
这种输入方式我也是第一次遇到,作为小白的我也深受震撼。
*/
while (cin >> c, c != ',')
s1 += c;
while (cin >> c)
s2 += c;
if (s.size() < s1.size() || s.size() < s2.size())
puts("-1");
//如果主字符串的长度要比两个副字符串还要小的话,那么就没有跨距的存在了,所以直接输出-1.
else
{
int l = 0;
/*这第一段的字符串匹配是在匹配字符串s1,在寻找字符串s1的位置,通过枚举的方法,来找到其位置,
起点从l=0开始,取和s1字符串长度相同的一段,从左到右依次枚举,直到l+s1.size()<=s.size()时
就可以停止枚举。
我这个代码是用for循环写的,所以定义起点l时要定义在外面,这是因为这个题要求字符串的最大跨距,
就是求确定位置后两点的举例,所以肯定会用到参数l和r,所以必须放外面,要是放在了里面,那么l和r就是
一个形参,要使用的时候就使用不到,所以这一点是在用for循环写的时候要注意的。这个for循环也可以改用
while循环来写,只不过最后的那个参数++啥子的就要注意加了,在哪个括号里面,在哪个结构里面,
就要注意了,这个很容易弄错或者搞忘,要注意。*/
/*int k = 0; 这一语句用于初始化变量 k,并在匹配过程中进行回溯。在循环中,当 a[j + k] 和 b[k] 不相等时,会执行 break; 跳出当前循环,并开始下一轮匹配。
通过这个回溯操作,可以确保每次开始新的匹配时,从字符串 b 的起始位置重新比较。如果不进行回溯,而是将 k 继续增加,那么在下一轮匹配时,a[j + k] 和 b[k] 的比较将从上一次不匹配的位置开始,而不是从 b 的起始位置开始。
因此,回溯操作能够确保每次匹配都从头开始,而不会错过可能的匹配。*/
for (; l + s1.size() <= s.size(); l++)
{
int k = 0;
for (; k < s1.size(); k++)
{
if (s[l + k] != s1[k])
break;
}/*发现没,如果匹配字符串中的字符要是每一个都匹配成功的话,那么压根就不会执行那个if语句。如果出现了两个字符不匹配的情况,那么就跳出这个for循环,然后再看此时的k值是多少。要真是k==s1.size()的话,那么就说明全部匹配成功,就找到了具体的位置。*/
if (k == s1.size())
/*再次提醒,这个if语句是k不断变动后,最后确定了才来判断,而不是把它放在循环里面一起运行,这个细节值得注意*/
/*这里我做个假设,假设我把这句放进那个内层循环,那么会出现什么情况呢?放进去那个内层循环后,意思就变了。假设s1的长度为2,那么在内循环里面的话,这个if语句的意思就变成了:当k等于2时,直接跳出内层循环。但是万一这内层循环第二个位置是能匹配的呢,这就会导致最后确定的位置出错,l会出错。*/
break;
/*总而言之,我们要的是匹配的字符数等于字符的长度,而不是匹配到第s1.size()这个位置直接跳出循环,这是不对的。*/
}
int r = s.size() - s2.size();
/*这个r是右边界枚举的形参,和上面的l一个性质。这里注意,其实右边界应该从字符串的末尾开始,从右往左的
依次枚举和匹配,直到找到右边界的位置。而这里却不是用的这个办法,但是也差不多。
主字符串的长度-副字符串s2的长度,得到的结果是不一样的,如果运气好,那么这一减,就直接找到了右边界
的位置,只需要匹配字符串,看是不是一样就行;第二种结果就是在右边界字符串的右边,但是不在右边界字符串
内;第三种结果第二种结果就是在右边界字符串的左边,但是不在右边界字符串内。无论哪一种情况,都不是确定
的。这样一减的话,那么遍历和匹配的次数就会减少,会提高程序的运行效率。
所以这个式子是必要的,然而,无论这个指向的是哪个位置,始终方向都是从右向左,所以最终不超过左边界点0
就好,所以才会有下面的条件r>=0。
*/
for (; r >= 0; r--)
{
int k = 0;
for (; k < s2.size(); k++)
//接下来有了关系式,那么仍和上面的s1一样,枚举和匹配字符串就行。
{
if (s[r + k] != s2[k])
break;
}
if (k == s2.size())
break;
}
l += s1.size() - 1;
/*这个式子就是我说的要引用l和r的地方。因为题上说的是字符串跨距是最右边的 S2 的起始点
与最左边的 S1 的终止点之间的字符数目。而我们之前匹配和枚举字符串的位置,就是l和r,都是字符串s1和s2的
首字母位置。而这个跨距的距离要的是字符串s1的末尾位置和字符串s2的首字母位置之间的字符数目,不包括s1字符串的末尾
那个字母和s2字符串的首字母,就中间那段距离。
*/
/*而这个位置是确定字符串s1的末字母位置,而之前的l是s1字符串的首位置,现在需要更新l,以便后面求
最大跨距,而字符串s2就不用更新r了,因为本就要的是s2字符串首字母的位置,所以就没有关于r的式子。*/
if (l >= r)
/*l>=r,说明枚举和匹配的时候l到了右边的地方,那么就说明字符串s1和s2有重复的部分。
你想想,l表示的是左边界的枚举,而r是表示右边界的枚举,l>=r,左边越过了右边,那怎么可能嘛,
一看就是错的。*/
puts("-1");
else
printf("%d\n", r - l - 1);
/*这里是直接输出最大跨距,因为不包含s1字符串的末字母和s2字符串的首字母,所以这个式子的
推导过程是:[(r-1)-(l+1)]+1,最终结果为r-l-1,就是上面那个式子。*/
}
return 0;
}
2.用while循环来写的,其实本质上和for循环一样。
#include
using namespace std;
int main()
{
string s, s1, s2;
char c;
while (cin >> c, c != ',') s += c;
while (cin >> c, c != ',') s1 += c;
while (cin >> c) s2 += c;
if (s.size() < s1.size() || s.size() < s2.size()) puts("-1");
else
{
int l = 0;
while (l + s1.size() <= s.size())
{
int k = 0;
while (k < s1.size())
{
if (s[l + k] != s1[k]) break;
k ++ ;
}
if (k == s1.size()) break;
l ++ ;
}
int r = s.size() - s2.size();
while (r >= 0)
{
int k = 0;
while (k < s2.size())
{
if (s[r + k] != s2[k]) break;
k ++ ;
}
if (k == s2.size()) break;
r -- ;
}
l += s1.size() - 1;
if (l >= r) puts("-1");
else printf("%d\n", r - l - 1);
}
return 0;
}
两种方法写的其实本质一样,值得注意的是有些小细节,不注意就会输出错误,代码里面的细节十分重要,希望各位注意。