hello,大家好,这里是bang___bang_,本篇记录2道牛客习题,公共子串计算(中等),通配符匹配(较难),如有需要,希望能有所帮助!
目录
1️⃣公共子串计算
公共子串计算_牛客题霸_牛客网 (nowcoder.com)
描述
给定两个只包含小写字母的字符串,计算两个字符串的最大公共子串的长度。
注:子串的定义指一个字符串删掉其部分前缀和后缀(也可以不删)后形成的字符串。数据范围:字符串长度:1≤s≤150
进阶:时间复杂度:O(n^3) ,空间复杂度:O(n)
输入描述:
输入两个只包含小写字母的字符串
输出描述:
输出一个整数,代表最大公共子串的长度
示例1:
输入: asdfas
werasdfaswer
返回值:6
解题思路:
1.计算最大公共子串的长度,需要形成第i-1位置的子串长度提供给第i位置的子串。故采用动态规划。dp[ i ] [ j ]表示 s[ i ] 与 p[ i ] 构成子串的长度
2.子串长度是在前面求得的子串的长度下再+1。递推公式:dp[ i ] [ j ] = dp[ i-1 ] [ j-1 ] + 1
3.在两待匹配字符串前添加一个空串,方便我们从两个待匹配字符串首字符开始遍历。遍历从下标1开始,即从待匹配字符串的首字符,并且防止 i-1 和 j-1 越界,默认初始化值为0,相当于空串并不加入计算,只是为了方便我们去匹配。
4.使用局部变量maxlen,在每次匹配为子串时进行比较更新为最大长度。
代码实现:
cpp
#include <iostream>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
int GetMaxLength(string s,string p)
{
int maxlen=0;//记录最大公共字串长度
s=" "+s;//添加空串
p=" "+p;//添加空串
int n=s.size();
int m=p.size();
vector<vector<int>>dp(n,vector<int>(m));//初始化为0,不影响计算长度
//i=1,j=1;从待匹配字符串开始匹配(原字符串)
for(int i=1;i<n;i++)
{
for(int j=1;j<m;j++)
{
if(s[i]==p[j])//匹配为子串
{
dp[i][j]=1+dp[i-1][j-1]; //获取长度
maxlen=max(maxlen,dp[i][j]);//更新最大长度
}
}
}
return maxlen;
}
int main() {
string str1,str2;
while(cin>>str1>>str2)
{
cout<<GetMaxLength(str1,str2)<<endl;
}
return 0;
}
2️⃣通配符匹配
描述
请实现支持'?'and'*'.的通配符模式匹配
'?' 可以匹配任何单个字符。
'*' 可以匹配任何字符序列(包括空序列)。返回两个字符串是否匹配
函数声明为:
bool isMatch(string s,string p)
下面给出一些样例: isMatch("aa","a") → falseisMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "d*a*b") → false数据范围:字符串长度满足 0≤n≤1000
进阶:空间复杂度 O(1),时间复杂度 O(n)
示例1:
输入:"ab","?*"
返回值:true
示例2:
输入:"ab","*"
返回值:true
解题思路: s为待匹配字符串,p为含通配符字符串
1.通配符匹配,前一个位置匹配的结果要给后一个位置进行判断是否匹配,故采用动态规划。
dp[ i ] [ j ] 表示s[ i ] 和 p [ j ]匹配情况
2.根据规则可以将匹配情况划分为2种:
【1】p [ j ] = * ,*号可以匹配0至多个。
【2】p [ j ] != * ,也就是说是相同字符或者是?号,?号相当于万能符,必定和待匹配字符串匹配,效果相当于相同字符匹配。
3.推导递推公式:
【1】当p [ j ] = * ;去掉 * 号匹配的字符,看*前匹配的结果。j-1 去掉*号,i-num 去掉 * 号匹配的字符。
步骤一: 如果 * 号匹配0个,dp[ i ] [ j ] = dp[ i ] [ j-1 ]
如果 * 号匹配1个,dp[ i ] [ j ] = dp[ i-1 ] [ j-1 ]
如果 * 号匹配2个,dp[ i ] [ j ] = dp[ i-2 ] [ j-1 ]
如果 * 号匹配.....
步骤二:* 号可以匹配0至n个字符,也就是说上述情况都有可能发生,也就是采用并集。
即dp[ i ] [ j ] = dp[ i ] [ j-1 ] || dp[ i-1 ] [ j-1 ] || dp[ i-2 ] [ j-1 ] || ..........1️⃣
步骤三:将 i = i-1 1️⃣
得到 dp[ i-1 ] [ j ] = dp[ i-1 ] [ j-1 ] || dp[ i-2 ] [ j-1 ] || dp[ i-3 ] [ j-1 ] || .......... 2️⃣
步骤四:发现2️⃣与1️⃣后部分一致,进行替换。
得到最终递推公式:当p [ j ] == * ;dp[ i ] [ j ] = dp[ i ] [ j-1 ] || dp[ i-1 ] [ j ]
【2】当p [ j ] != *;dp[ i ] [ j ] = dp[ i-1 ] [ j-1 ] && (s[ i ] == p [ j ] || p[ j ] == '?' )前一个字符的匹配结果与当前匹配是否为相同字符或者通配符?共同决定当前位置的匹配结果。
4.添加空串,空串必定匹配设置为true;原s串待匹配串,不可能和添加的空串匹配,默认为false;如果原p串首字符为*有可能和添加的空串匹配,所以要从[0][1]开始匹配。
|---|---|----|---|---|
| | | * | ? | a |
| | T | T | F | F |
| a | F | | | |
| b | F | | | |
| a | F | | | |
代码实现:
cpp
class Solution {
public:
bool isMatch(string s, string p) {
//添加空串
s = " " + s;
p = " " + p;
int n = s.size();
int m = p.size();
vector<vector<bool>>dp(n, vector<bool>(m));//默认初始化为false
dp[0][0] = true;//空串必定匹配设置true
//原s串待匹配串,不可能和添加的空串匹配,i!=1,[i][0]默认为false
for (int i = 0; i < n; i++)
{
//如果原p串首字符为*有可能和添加的空串匹配,所以要从[0][1]开始匹配
for (int j = 1; j < m; j++)
{
if (p[j] != '*') {
//i&&j防止越界
dp[i][j] = i && j && dp[i - 1][j - 1] && (s[i] == p[j] || p[j] == '?');
} else {
//i&&和j&&防止越界
dp[i][j] = (i && dp[i - 1][j]) || (j && dp[i][j - 1]);
}
}
}
return dp[n - 1][m - 1];
}
};
文末结语,本篇记录了2道动态规划的牛客习题,1道中等题(公共子串计算);1道较难题(通配符匹配),包含递推公式的推导,本文旨在记录,如有需要,希望能有所帮助!!