字符串最大跨距

有三个字符串 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;
}

 两种方法写的其实本质一样,值得注意的是有些小细节,不注意就会输出错误,代码里面的细节十分重要,希望各位注意。

你可能感兴趣的:(c++,visual,studio,code)