给你一个文本串 T ,一个非空模板串 S ,问 S 在 T 中出现了多少次
数据范围:
要求:空间复杂度 ,时间复杂度
function kmp(S, T) { if (!S || !T || T.length < S.length) return 0; const next = getNext(S); return getCount(next, S, T); } function getNext(needle) { const next = Array(needle.length).fill(0); for (let i = 1, j = 0; i < needle.length; ++i) { while (j > 0 && needle[i] !== needle[j]) j = next[j - 1]; if (needle[i] === needle[j]) j++; next[i] = j; } return next; } function getCount(next, needle, haystack) { let count = 0; for (let i = 0, j = 0; i < haystack.length; i++) { while (j > 0 && haystack[i] !== needle[j]) j = next[j - 1]; if (haystack[i] === needle[j]) ++j; if (j === needle.length) { count++; j = next[j - 1]; } } return count; }
/** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算模板串S在文本串T中出现了多少次 * @param S string字符串 模板串 * @param T string字符串 文本串 * @return int整型 */ int kmp(char* S, char* T ) { // write code here int a,i,j,n=0; a=strlen(S); for(i=0;T[i]!='\0';i++) { for(j=0;j<a;j++) { if(S[j]!=T[i+j]) { break; } } if(j>=a) { n++; } } return n; }
public class Solution { public int kmp (String S, String T) { int res = 0; int[] next = new int[S.length()]; getNext(next, S); int j = 0; int i = 0; while (i < T.length()) { // 退到头或相等,i指针往后 if (j == 0 || T.charAt(i) == S.charAt(j)){ i++; j++; } else { // j回退,防止溢出 if (j == 0) j = 0; else j = next[j - 1]; } if (j == S.length()){ // 找到一个子串,j回退 res++; j = next[j - 1]; } } return res; } void getNext(int next[], String S){ int j = 0; next[0] = 0; // 初始化 for (int i = 1; i < S.length(); i++) { // 前缀不相同时;注意此处回退循环,退到相等 while (j > 0 && S.charAt(i) != S.charAt(j)) j = next[j - 1]; // 前缀相同时,更新前缀指针和next数组 if (S.charAt(i) == S.charAt(j)) { j++; next[i] = j; } } } }人家三个学者提出来的算法,没提前学过或专门学过,基本没人能直接写出来。完整学过理解之后才能模仿实现,这种题也能算中等题?。。
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算模板串S在文本串T中出现了多少次 * @param S string字符串 模板串 * @param T string字符串 文本串 * @return int整型 */ public int kmp (String S, String T) { return find(T,S); } int find(String source,String pattern){ int n1=source.length(); int n2=pattern.length(); char []par=pattern.toCharArray(); char[]sour=source.toCharArray(); int[]next=new int[n2]; int k=-1; next[0]=-1; //构建next数组,其中第next[i]表示par模式串数组中最长公共前缀后缀的前缀结束的下标 // par="aabaac" next[0]=-1 next[1]=1 next[2]=-1 next[3]=1 next[4]=1 next[5]=-1 for(int i=1;i<n2;i++){ while(k!=-1&&par[k+1]!=par[i])k=next[k]; if(par[k+1]==par[i])k++; next[i]=k; } k=-1; int ans=0; for(int j=0;j<n1;j++){ //如果匹配 j++,k++ if(sour[j]==par[k+1])k++; else{ //不匹配则在模式串中,继续向前搜索 //比如pattern aabaac //待查找的串 aaeefaagef // aeefaagef aeefaagef // aabaac aabaac // |该位置不匹配 next[1]=0| while(k!=-1&&par[k+1]!=sour[j])k=next[k]; } if(k==n2-1){ int pre=j-pattern.length()+2; ans++; j=pre; k=-1; } } return ans; } }
/** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算模板串S在文本串T中出现了多少次 * @param S string字符串 模板串 * @param T string字符串 文本串 * @return int整型 */ function kmp( S , T ) { var index = T.indexOf(S); var count = 0; while(index > -1) { count++; index = T.indexOf(S, index + 1); } return count; } module.exports = { kmp : kmp };
class Solution: def kmp(self , S , T ): # kmp算法参考leetcode 28 # 不同于kmp,这里还需统计匹配的次数 m = len(T) n = len(S) # 创建next数组 nex = [0] * n count = 0 j = 0 # 计算next数组 for i in range(1, n): while j > 0 and S[j] != S[i]: j = nex[j-1] if S[j] == S[i]: j += 1 nex[i] = j # kmp字符串匹配 j = 0 for i in range(m): while j > 0 and S[j] != T[i]: j = nex[j-1] if S[j] == T[i]: j += 1 # 判断是否完成完整匹配 if j == n: count += 1 j = nex[j-1] # 返回出现个数 return count
class Solution { public: int kmp(string S, string T) { // write code here int ans=0;//记录出现次数 //先求next数组-前缀表 int next[S.size()]; next[0]=0; int j=0; for(int i=1;i<S.size();i++){ //前后缀不相同时 while(j>0&&S[i]!=S[j]){ j=next[j-1];//回退 } //前后缀相同时 if(S[i]==S[j]){ j++; next[i]=j; } } //看模板串在文本串出现了几次 j=0; for(int i=0;i<T.size();i++){ while(j>0&&T[i]!=S[j]){ j=next[j-1];//回退 } if(T[i]==S[j]) j++; if(j==S.size()){//j==S.size():说明已经遍历完找到一个了 ans++; j=next[j-1]; } } return ans; } };
public int kmp (String S, String T) { final int sLength = S.length(); final AtomicInteger count = new AtomicInteger(); Queue<Character> sQueue = new LinkedList<>(); Queue<Character> tQueue = new LinkedList<>(); IntStream.range(0, sLength).forEach(index -> { sQueue.offer(S.charAt(index)); tQueue.offer(T.charAt(index)); }); if (sQueue.hashCode() == tQueue.hashCode()) { count.getAndIncrement(); } IntStream.range(0, T.length()-sLength).forEach(index -> { tQueue.poll(); tQueue.offer(T.charAt(index + sLength)); if (sQueue.hashCode() == tQueue.hashCode()) { count.getAndIncrement(); } }); return count.get(); }
class Solution: def kmp(self, S: str, T: str) -> int: # write code here # 运行超时 len_s = len(S) len_t = len(T) res = 0 for i in range(len_t - len_s + 1): if S == T[i : i + len_s]: res +=1 return res
import java.util.*; public class Solution { public int kmp(String S, String T) { int M = S.length(); int N = T.length(); // 创建lps[]数组,保存模式串的前缀函数 int[] lps = LPS(S); // 初始化两个指针i和j,分别指向文本T和模式串S的当前位置 int i = 0; int j = 0; // 初始化计数器count,用于记录模式串S在文本T中出现的次数 int count = 0; // 遍历文本T,直到指针i到达文本的末尾 while (i < N) { // 如果模式串S的当前字符与文本T的当前字符相同,则两个指针都向前移动 if (S.charAt(j) == T.charAt(i)) { j++; i++; } // 如果模式串S已经完全匹配,则增加计数器,并根据lps数组将模式串的指针j回溯到合适的位置 if (j == M) { count++; j = lps[j - 1]; } else if (i < N && S.charAt(j) != T.charAt(i)) { // 如果当前字符不匹配,且模式串的指针j不为0,则根据lps数组将模式串的指针j回溯 if (j != 0) j = lps[j - 1]; // 如果模式串的指针j为0,则只能将文本的指针i向前移动 else i = i + 1; } } return count; } // 定义一个私有静态方法,用于计算模式串的前缀函数数组lps[] private static int[] LPS(String pattern) { // 初始化lps数组,长度与模式串相同 int[] lps = new int[pattern.length()]; // 初始化len为0,用于记录前缀的长度 int len = 0; // 初始化i为1,因为lps[0]总是为0 int i = 1; // lps[0]设置为0,因为模式串的第一个字符总是前缀 lps[0] = 0; // 遍历模式串,计算每个位置的前缀长度 while (i < pattern.length()) { // 如果当前字符与前缀的最后一个字符相同,则更新len和lps[i] if (pattern.charAt(i) == pattern.charAt(len)) { len++; lps[i] = len; i++; } else { // 如果当前字符与前缀的最后一个字符不同,且len不为0,则根据lps数组回溯len if (len != 0) { len = lps[len - 1]; } else { // 如果len为0,则更新lps[i]为0,并向前移动i lps[i] = 0; i++; } } } return lps; } }
/** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算模板串S在文本串T中出现了多少次 * @param S string字符串 模板串 * @param T string字符串 文本串 * @return int整型 */ #include <string.h> int ne[100000]; void getNext(char* a, int next[]) { int j = 0; next[0] = 0; for(int i = 1; i < strlen(a); i++) { while(j > 0 && a[i] != a[j]) { j = next[j - 1]; //回到前一个,相当于自身做了一次kmp } if(a[i] == a[j]) { j++; } next[i] = j; } } int kmp(char* S, char* T ) { // write code here int i = 0 , j = 0, count = 0; int slen = strlen(S); int tlen = strlen(T); getNext(S, ne); for(i = 0; i < tlen; i++) { if(j == 0 && tlen - i < slen) break; while(j > 0 && T[i] != S[j]) { j = ne[j - 1]; } if(T[i] == S[j]) { j++; } if(j == slen) { count++; j = ne[j - 1]; } } return count; }还是跑不完,只能跑5组测试用例,数一大还是超时,还望老哥们赐教
// 参考B站左程云大佬的KMP算法讲解 public int kmp (String S, String T) { // write code here char []s1 = S.toCharArray(); char []s2 = T.toCharArray(); int n = s1.length, m = s2.length; if (n > m || m == 0) return 0; int cnt = 0; int x = 0, y = 0; int index = 0; int []next = nextArray(s1, n); while (x < m) { if (y == n-1&&s1[y] == s2[x]) { cnt++; y=next[y]; } if (s1[y] == s2[x]) { x++; y++; } else if (y == 0) { x++; } else { y = next[y]; } } return cnt; } /** 得到next数组 */ public int[] nextArray(char []s, int m) { if (m == 1) { return new int[] {-1}; } int []next = new int[m]; next[0] = -1; next[1] = 0; int i = 2, cn = 0; while (i < m) { if (s[i - 1] == s[cn]) { next[i++] = ++cn; } else if (cn > 0) { cn = next[cn]; } else { next[i++] = 0; } } return next; }
// 超时算法 public int kmp (String S, String T) { // write code here int ls=S.length() ,lt=T.length() ,res=0; for(int i=0;i+ls<=lt;i++){ if(S.equals(T.substring(i,i+ls))){ res++; } } return res; }
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算模板串S在文本串T中出现了多少次 * @param S string字符串 模板串 * @param T string字符串 文本串 * @return int整型 */ public int kmp (String S, String T) { // write code here /** 暴力解 int count = 0; for(int i = 0; i <= T.length() - S.length(); i++) { int len = 0; for(; len < S.length(); len++) { if(S.charAt(len) != T.charAt(i + len)) break; } if(len == S.length()) count++; } return count; */ int[] next = new int[S.length()]; getNext(next, S); int res = 0; int startS = 0; for(int i = 0; i < T.length(); i++) { while((startS > 0) && (S.charAt(startS) != T.charAt(i))) { startS = next[startS - 1]; } // 两种情况,0都不匹配,那就下一个i从头开始 if(S.charAt(startS) == T.charAt(i)) startS++; // 匹配成功,重置 if(startS == S.length()) { res++; startS = next[startS - 1]; } } return res; } // 前缀数组初始化 private void getNext(int[] next, String s) { // next[i]的含义就是(0 - i)串的最大前缀后缀和 // 实际上也是dp递归思路求解 int endPrefix = 0; for(int i = 1; i < next.length; i++) { while((endPrefix > 0) && (s.charAt(endPrefix) != s.charAt(i))) { endPrefix = next[endPrefix - 1]; } int maxLen = endPrefix; if(s.charAt(endPrefix) == s.charAt(i)) { maxLen = maxLen + 1; endPrefix++; } next[i] = maxLen; } } }