https://leetcode.com/problems/bold-words-in-string/description/
给定一个长
n
n
n的字符串
s
s
s和一个单词列表
A
A
A,单词列表里的词都需要做加粗记号。求加上加粗记号的最终字符串。加粗记号指的是在
s
s
s的加粗的部分两边加上"",""。注意,对于加粗记号,必须保证这个记号是包括了最长的一段,且不要嵌套,即加粗记号的紧邻的左右两边的字符必定不加粗。题目保证所有出现的字符只有英文字母和数字。题目保证
A
A
A中字符串的长度范围是
[
1
,
10
]
[1, 10]
[1,10]。
正解是AC自动机,参考https://blog.csdn.net/qq_46105170/article/details/128060295。也可以用字符串哈希来做。先求出 A A A中所有字符串的哈希值,并且记录一下 A A A中最短和最长的字符串长度,再求 s s s的哈希数组。接着枚举 s [ i ] s[i] s[i],看以 s [ i ] s[i] s[i]结尾是否能取到 A A A中的字符串,可以直接枚举长度,并且长度从大到小枚举(如果长度大的已经覆盖了,小的就不用继续枚举了)。匹配的过程可以直接比较哈希值。代码如下:
class Solution {
public:
using UL = unsigned long;
string boldWords(vector<string>& ws, string s) {
UL P = 131;
int n = s.size();
vector<UL> ha(n + 1), pow(n + 1);
pow[0] = 1;
for (int i = 0; i < n; i++) {
ha[i + 1] = ha[i] * P + s[i];
pow[i + 1] = pow[i] * P;
}
int M = 1, m = 10;
unordered_set<UL> st;
for (auto& w : ws) {
M = max(M, (int)w.size());
m = min(m, (int)w.size());
UL h = 0;
for (char ch : w) h = h * P + ch;
st.insert(h);
}
vector<bool> mark(s.size());
for (int i = m - 1; i < n; i++)
// 只枚举长度在[m, M]的范围
for (int j = i - M + 1; j <= i - m + 1; j++) {
if (j < 0) continue;
UL h = ha[i + 1] - ha[j] * pow[i - j + 1];
if (st.count(h)) {
for (int k = j; k <= i; k++) mark[k] = true;
break;
}
}
string res;
for (int i = 0; i < n; i++)
if (!mark[i]) res += s[i];
else {
res += "";
while (i < n && mark[i]) res += s[i++];
res += "";
i--;
}
return res;
}
};
时间复杂度 O ( ∑ l A i + n max l A i ) O(\sum l_{A_i}+n\max l_{A_i}) O(∑lAi+nmaxlAi),空间 O ( l A + n ) O(l_A+n) O(lA+n)。