您的位置:首页 > 其它

LeetCode 30 Substring with Concatenation of All Words(AC自动机+滑动窗口)

2016-03-23 16:01 453 查看
题意:给出一个字符串,和一个字典,字典中有若干个单词(可能有重复单词),每个单词的长度相等,现在要求所有字典中全部单词的任意组合在字符串中的起始下标。

思路:首先先说一下用ac自动机复杂度可以优化到O(n)啊!!!!!!看了votes最高的答案,复杂度是O(n*k)(n为字符串长度,k为字典中单词长度)。

现在 说一下做法,设字符串长度为n,字典中有m个单词,每个单词长度为w。

朴素的做法是对于每个位置,对于从它向后的m*w个位置进行判断,看看是否符合要求,这样做的时间复杂度为O(n*m*w),

那么怎么优化呢,可以用滑动窗口的思想,用两个指针标记一下窗口的起止位置,在窗口中的单词为当前符合要求的单词序列,每次根据情况判断当前右指针指向的单词是否符合条件,一下使窗口正确滑动就行了,这样做的话时间复杂度可以优化到O(n*w)

然后就是用ac自动机了,因为上一步每次还要判断单词是否是字典中的一个合法单词,所以复杂度中多了一个w,如何去掉它呢,可以用ac自动机预处理一下,

处理一下字符串中每一个位置是哪个单词的开始位置,然后我们每次就不用用O(w)的时间去判断当前有指针所指单词是否是一个合法单词了,O(1)判断一下就行了,ac自动机预处理的复杂度为O(m*w+n),所以总的复杂度为O(m*w+n),比最高票答案要好很多啊!!!!!!!!!!

代码写得有点挫

class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
struct Aho_Corasick {
int sz;
int ch[10000][27];
int val[10000];
int f[10000];
int lens;
unordered_map<int, int> flag;

int idx(char c) {return c - 'a';}
Aho_Corasick(string s) {
sz = 1;
lens = s.length();
memset(ch[0], 0, sizeof(ch[0]));
};
void insert(string str, int v) {
int u = 0, n = str.length();
for(int i = 0; i < n; i++) {
int c = idx(str[i]);
if(!ch[u][c]) {
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = v;
}

int getFail() {
queue<int> q;
f[0] = 0;
for(int c = 0; c < 26; c++) {
int u = ch[0][c];
if(u) {
f[u] = 0;
q.push(u);
}
}
while(!q.empty()) {
int r = q.front(); q.pop();
for(int c = 0; c < 26; c++) {
int u = ch[r][c];
if(!u) {
ch[r][c] = ch[f[r]][c];
continue;
}
q.push(u);
int v = f[r];
while(v && !ch[v][c]) v = f[v];
f[u] = ch[v][c];
}
}
}

void find_T(string s, int lenw) {
int j = 0;
for(int i = 0; i < lens; i++) {
int c = idx(s[i]);
j = ch[j][c];
if(val[j]) flag[i-lenw+1] = val[j];
}
}
};
vector<int> ans;
Aho_Corasick ac = Aho_Corasick(s);
int cnt = words.size();
int lenw = words[0].length();
unordered_map<string, int> dic;
unordered_map<int, int> num;
int id = 0;
for (int i = 0; i < cnt; i++) {
if(!dic.count(words[i]))
dic[words[i]] = ++id;
num[dic[words[i]]]++;
}
for (int i = 0; i < cnt; i++)
ac.insert(words[i], dic[words[i]]);
ac.getFail();
ac.find_T(s, lenw);
for (int i = 0; i < lenw; i++) {
int left = i;
int word_cnt = 0;
unordered_map<int, int> tmp_cnt;
for (int j = i; j < ac.lens; j+=lenw) {
if (!ac.flag[j]) {
left = j + lenw;
word_cnt = 0;
tmp_cnt.clear();
}
else {
int v = ac.flag[j];
tmp_cnt[v]++;
word_cnt++;
if (tmp_cnt[v] > num[v]) {
do {
tmp_cnt[ac.flag[left]]--;
left += lenw;
word_cnt--;
} while (ac.flag[left-lenw] != v);
}
if (word_cnt == cnt)
ans.push_back(left);
}
}
}
return ans;
}

};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息