【蓝桥】契合匹配

一、题目

1、题目描述

小蓝有很多齿轮,每个齿轮的凸起和凹陷分别用一个字符表示,一个字符串表示一个齿轮。

【蓝桥】契合匹配_第1张图片
如果这两个齿轮的对应位分别是同一个字母的大小写,我们称这个两个齿轮是契合的。

例如:AbCDeFghaBcdEfGH 就是契合的,但是 abcaBC不是契合的。

这天,小蓝的弟弟小桥从抽屉里拿来了两个齿轮,小蓝想知道,这两个齿轮是不是契合的。

特别需要注意的是,齿轮是环形的,所以是可以旋转的(顺时针和逆时针均可),如果是契合的,小蓝还想让你告诉他,最少将第一个齿轮旋转多少位,两个齿轮可以完全契合在一起。

例如:AbbCdBcDaB,在将第一个齿轮逆时针旋转两位后,变成 bCdAb,两个齿轮就完全契合在一起了。

输入格式

第一行输入一个正整数 n n n,代表两个齿轮的长度。

第二行输入一个长度为 n n n 的字符串 S S S,代表第一个齿轮。

第三行输入一个长度为 n n n 的字符串 T T T,代表第二个齿轮。

输出格式

第一行输出一个字符串:Yes 或者 No。代表两个齿轮是否契合。

如果可以契合,第二行输出一个整数,代表需要旋转的位数。

如果不可以契合,不用多余输出。

样例输入

5
AbbCd
BcDaB

样例输出

Yes
2

评测数据范围

数据范围: 1 ≤ n ≤ 1 0 6 1 \le n \le 10^6 1n106

保证字符串只包含大小写字母。

2、基础框架

#include 
using namespace std;
int main()
{
  // 请在此输入您的代码
  return 0;
}

3、原题链接

契合匹配

二、解题报告

1、思路分析

首先可以做的事是将 S S S 串大小写转换,那么就变成了两个字符串的完全匹配。

首先,使用破环成链的思想,将 S S S 串扩大两倍成为 S ′ S' S,将 T T T 串与 S ′ S' S 做匹配。如下代码:

// 将S串扩大两倍
for (int i = 1; i <= n; ++i) {
    if (s[i] >= 'a' && s[i] <= 'z') {
        s[i] += 'A' - 'a';
    } else if (s[i] >= 'A' && s[i] <= 'Z') {
        s[i] += 'a' - 'A';
    }
    s[i + n] = s[i];
}

然后使用 KMP 算法进行匹配,提供 KMP 算法代码:

void get_next(char *s, int *next, int n) {
    next[1] = 0;
    for (int i = 2, j = 0; i <= n; ++i) {
        while (j > 0 && s[j + 1] != s[i]) j = next[j];
        if (s[i] == s[j + 1]) ++j;
        next[i] = j;
    }
}

之后考虑匹配到的最前与最后的位置: P o s b e g i n Pos_{begin} Posbegin P o s e n d Pos_{end} Posend

然后对于两个位置,考虑顺时针与逆时针旋转需要的步数,取最优解即可。

算法本身并不复杂,主要考虑破环成链思想和 KMP 算法。

接下来简单介绍破环成链:

如果一个字符串 S S Sabcdef )扩展成两倍 S ′ S' Sabcdefabcdef ),那么从 S ′ S' S 的第二个字符开始 6 6 6 个字符组成的子串( bcdefa ),实际上就是 S S S 逆时针旋转 1 1 1 位的字符串,同时也是顺时针旋转 5 5 5 位的情况。

本题实质上就是 判断两个字符串是否互为旋转字符串。

对于KMP的讲解与实现见博客 KMP算法讲解与实现。

2、时间复杂度

O ( N ) O(N) O(N)

3、代码详解

/*************************************************************************
	> File Name: my5_契合匹配.cpp
	> Author: Maureen 
	> Mail: [email protected] 
	> Created Time: 日 10/15 18:48:14 2023
 ************************************************************************/

#include 
using namespace std;

//大写转换成小写,小写转换成大写
void strConvert(string &s) {
    for (int i = 0; i < s.size(); i++) {
        if (s[i] >= 'A' && s[i] <= 'Z') {
            s[i] += 'a' - 'A'; 
        } else if (s[i] >= 'a' && s[i] <= 'z') {
            s[i] += 'A' - 'a'; 
        }
    }
}

int *getNextArray(string &t) { 
    int *next = new int[t.size()];

    if (t.size() == 1) {
        next[0] = -1;
        return next;
    }
    
    next[0] = -1;
    next[1] = 0;

    int ind = 2; //当前待求信息的位置
    int cn = 0; //前缀的下一个位置,也是要与ind-1位置进行匹配的位置

    while (ind < t.size()) {
        if (t[cn] == t[ind - 1]) {
            next[ind++] = ++cn;
        } else if (cn > 0) {
            cn = next[cn];
        } else {
            next[ind++] = 0;
        }
    }

    return next;
}

int kmp(string &s, string &t) {

    int si = 0;
    int ti = 0;
    
    int *next = getNextArray(t);

    while (si < s.size() && ti < t.size()) {
        if (s[si] == t[ti]) {
            si++;
            ti++;
        } else if (next[ti] == -1) {
            si++;
        } else {
            ti = next[ti];
        }
    }
    
    return ti == t.size() ? si - ti : -1;
}

int main() {
    int n;
    cin >> n;

    string s;
    string t;
    cin >> s >> t;

    int oldSize = s.size();

    //s字符串进行大小写转换
    strConvert(s);

    //扩充为两倍
    s += s;
    
    int posBegin = kmp(s, t);
    int posEnd = oldSize + posBegin; 

    if (posBegin != -1) {
        cout << "Yes" << endl;
        cout << min(posBegin - 0, 2 * oldSize - posEnd);
    } else {
        cout << "No" << endl;
    }
    return 0;
}

你可能感兴趣的:(#,蓝桥,#,常用算法,蓝桥,KMP)