题目
{% fold 点击显/隐题目 %}
题解
正则表达式
基本上可以在读题都一半的时候确定是正则表达式的问题
通常来说,这种问题一半会使用AC自动机来解答,不过其实可以试一下C++11的正则表达式库的
首先确定题意的时候可以发现,这里和标准的正则表达式语句存在一些不同
.*
在标准的正则表达式中是匹配任意字符串,而这里是匹配任意的连续字符串
相当于 (.)?\1*
当然,还需要考虑*
可以匹配0
次,但是这是正则表达式本身就有的语法
因此,简单思路就是先把 .*
替换成 (.)?\1*
(在源码里要写成(.)?\\1*
)
然后直接匹配即可
相应部分的代码如下
具体实现代码见正则表达式源码
需要特别注意的是,虽然同样是无脑用库,但是Java会超时
另外C++11的Regex库存在的bug比较多,只有比较简单的匹配才能放心使用
比如aba
匹配abababa
是无法得到3的,无法使用类似这样的(?=(aba))
零宽断言语法来实现(其他语言可以)
动态规划
按照动态规划的思路,很容易得到dp[i][j]
表示字符串a的前i
个字符和字符串b
的前j
的字符能否匹配
假如我们要匹配 aaaaaa
和 ab*a*
对于第一个位置(最左上角),显然当两者相等的时候为true
其同一行已经没有能够匹配的了
第二行可以发现b
不等于a
全部都是false
第三行的*
可以将上一行的b
匹配0
次,这时,当前位置能否匹配成功取决于它上面的上面是否能匹配
第四行有a
和a
相同,位置能否匹配取决于其左上角的能否匹配
第五行的*
可以匹配任意长度,匹配两个时候相当于该位置取决于上面能否匹配;匹配更多个的时候相当于只要其对应的a
中的字符和它左面的相同,就可以将左面能否匹配的状态传递过来
另外,.
可以看作一个普通字符,只是能和所有的字符都匹配
综上所述,总共只包含很少的状态转移条件
- 串内容相同(或者存在'.'或者为第一个位置):
dp[i][j] |= dp[i-1][j-1]
- 匹配到
*
:
dp[i][j] |= dp[i][j-2] | dp[i][j-1]
- 匹配到
*
,并且当前位置和其左面的字符相同:
dp[i][j] |= dp[i-1][j]
这样就可以写出基本的dp框架了
然后就会发现我们漏掉了一个特殊情况
bbbbbb
匹配a*b*
由于开始位置匹配失败,导致后面所有位置都无法为true
只有b[1]=='*'
的时候才会导致这个错误
相当于特殊情况下的状态转移1
其条件如下: 串内容相同或者存在.
,同时i=0
,b[1]='*'
这样就可以写出完整的状态转移方程了
代码
正则表达式
{% fold 点击显/隐代码 %}```cpp Two strings https://github.com/OhYee/sourcecode/tree/master/ACM 代码备份
#include
#include
#include
using namespace std;
void string_replace(string &s1, const string &s2, const string &s3) {
string::size_type pos = 0;
string::size_type a = s2.size();
string::size_type b = s3.size();
while ((pos = s1.find(s2, pos)) != string::npos) {
s1.replace(pos, a, s3);
pos += b;
}
}
int main() {
cin.tie(0);
cin.sync_with_stdio(false);
int T;
cin >> T;
while (T--) {
string text, re;
cin >> text >> re;
//cout << text << endl << re << endl;
string_replace(re, ".*", "(.)?\\1*");
// cout << text <<" "<< re << endl;
regex e(re);
cout << (regex_match(text, e) ? "yes" : "no") << endl;
}
return 0;
}
{% endfold %} ## 动态规划 {% fold 点击显/隐代码 %}```cpp Two strings https://github.com/OhYee/sourcecode/tree/master/ACM 代码备份 #include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxn = 3505; char a[maxn], b[maxn]; bool dp[maxn][maxn]; int main() { cin.tie(0); cin.sync_with_stdio(false); int T; cin >> T; // scanf("%d", &T); // getchar(); while (T--) { cin >> a >> b; // scanf("\n%[^\n]\n%[^\n]", a, b); // fgets(a,maxn,stdin); // fgets(b,maxn,stdin); memset(dp, false, sizeof(dp)); // printf("%d\na:%s\nb:%s\n",T, a, b); int n = strlen(a); int m = strlen(b); for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j) { // 第一个位置 if ((a[i] == b[j] || b[j] == '.') && i == 0 && j == 0) dp[i][j] |= true; // 从左上角转移过来 if ((a[i] == b[j] || b[j] == '.') && i > 0 && j > 0) dp[i][j] |= dp[i - 1][j - 1]; // 特判 if ((a[i] == b[j] || b[j] == '.') && b[1] == '*' && i == 0) dp[i][j] = true; // * 匹配 // 从上面的上面转移过来(匹配0个) //从上面转移过来(匹配1个) if (b[j] == '*') dp[i][j] |= (j >= 2 ? dp[i][j - 2] : 0) | (j >= 1 ? dp[i][j - 1] : 0); // 从左面转移过来(*往后续) if (b[j] == '*' && i >= 1 && a[i - 1] == a[i]) dp[i][j] |= dp[i - 1][j]; } // for (int i = 0; i < n; ++i) { // for (int j = 0; j < m; ++j) // printf("%d ", dp[i][j]); // printf("\n"); // } // printf("%s\n", (dp[n - 1][m - 1] ? "yes" : "no")); cout << (dp[n - 1][m - 1] ? "yes" : "no") << endl; } return 0; }
{% endfold %}