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

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

思路:首先先说一下用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 findSubstring(string s, vector& words) {
		struct Aho_Corasick {   
		    int sz;  
		    int ch[10000][27];  
			int val[10000]; 
			int f[10000];
			int lens;
            unordered_map 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 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 ans;
		Aho_Corasick ac = Aho_Corasick(s);
		int cnt = words.size();
		int lenw = words[0].length();
		unordered_map dic;
		unordered_map 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 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;
    }
    
    
};


你可能感兴趣的:(程序设计竞赛)