String 在线性时间内匹配字符串的模式

String 在线性时间内匹配字符串的模式,string,algorithm,pattern-matching,String,Algorithm,Pattern Matching,给定两个字符串S和T,其中T是模式字符串。查找模式字符串的任何加扰形式是否作为子字符串存在于字符串S中,如果存在,则返回起始索引 例如: 字符串S:abcdef 字符串T:efd 字符串S有def,这是搜索字符串T:efd的组合 我找到了一个运行时间为Om*n的解决方案。我正在研究一个线性时间解决方案,其中我使用HashMap静态的一个,为字符串T维护,另一个是前一个HashMap的动态副本,用于检查T的当前子字符串。我将在下一个失败的字符处开始检查。但在最坏的情况下,这在Om*n中运行。 我想

给定两个字符串S和T,其中T是模式字符串。查找模式字符串的任何加扰形式是否作为子字符串存在于字符串S中,如果存在,则返回起始索引

例如:

字符串S:abcdef 字符串T:efd

字符串S有def,这是搜索字符串T:efd的组合

我找到了一个运行时间为Om*n的解决方案。我正在研究一个线性时间解决方案,其中我使用HashMap静态的一个,为字符串T维护,另一个是前一个HashMap的动态副本,用于检查T的当前子字符串。我将在下一个失败的字符处开始检查。但在最坏的情况下,这在Om*n中运行。
我想得到一些指针,让它在Om+n时间内工作。任何帮助都将不胜感激。

如果字母表不是太大,比如ASCII,那么就不需要使用哈希来处理字符串


只要使用一个与字母表大小相同的大数组,存在性检查就会变成O1。因此,整个算法变成Om+n。

如果字母表不是太大,比如ASCII,那么就不需要使用哈希来处理字符串


只要使用一个与字母表大小相同的大数组,存在性检查就会变成O1。因此,整个算法变成Om+n。

首先,我想知道字符串S长度m和模式T长度n的边界

虽然有一个普遍的想法,但基于它的解决方案的复杂性取决于模式长度。对于长度为2的短模式,复杂度从Om到Om*n^2不等;b->3,c->4,我们无法破译示例4——是c还是aa

短模式案例的解决方案: 对于字符串S,我们应该计算所有前缀的线性时间素数积。我的意思是我们必须创建一个数组,使得A[0]=primeProductS[0],A[1]=primeProductS[0]S[1],A[N]=primeProductS。示例实现:

搜索模式T。计算primeProductT。对于与模式具有相同长度的所有“窗口”,请将其primeProduct与primeProductpattern进行比较。如果currentWindow等于模式或currentWindow是一个经过筛选的表单,则模式primeProducts的语法将是相同的

重要提示!我们已经准备了一个数组A,用于快速计算S[i]、S[i+1]、…S[j]=getPrimeS[i]*…*getPrimeS[j]=A[j]/A[i-1]的任何子串的素数积


复杂性:如果模式长度首先,我想知道字符串S长度m和模式T长度n的边界

虽然有一个普遍的想法,但基于它的解决方案的复杂性取决于模式长度。对于长度为2的短模式,复杂度从Om到Om*n^2不等;b->3,c->4,我们无法破译示例4——是c还是aa

短模式案例的解决方案: 对于字符串S,我们应该计算所有前缀的线性时间素数积。我的意思是我们必须创建一个数组,使得A[0]=primeProductS[0],A[1]=primeProductS[0]S[1],A[N]=primeProductS。示例实现:

搜索模式T。计算primeProductT。对于与模式具有相同长度的所有“窗口”,请将其primeProduct与primeProductpattern进行比较。如果currentWindow等于模式或currentWindow是一个经过筛选的表单,则模式primeProducts的语法将是相同的

重要提示!我们已经准备了一个数组A,用于快速计算S[i]、S[i+1]、…S[j]=getPrimeS[i]*…*getPrimeS[j]=A[j]/A[i-1]的任何子串的素数积


复杂性:如果模式长度,这个JavaScript示例是线性时间吗

<script>

function matchT(t,s){
  var tMap = [], answer = []

  //map the character count in t
  for (var i=0; i<t.length; i++){
    var chr = t.charCodeAt(i)

    if (tMap[chr]) tMap[chr]++
    else tMap[chr] = 1
  }

  //traverse string
  for (var i=0; i<s.length; i++){
    if (tMap[s.charCodeAt(i)]){
      var start = i, j = i + 1, tmp = []

      tmp[s.charCodeAt(i)] = 1

      while (tMap[s.charCodeAt(j)]){
        var chr = s.charCodeAt(j++)

        if (tmp[chr]){
          if (tMap[chr] > tmp[chr]) tmp[chr]++
          else break
        }
        else tmp[chr] = 1
      }

      if (areEqual (tmp,tMap)){
        answer.push(start)
        i = j - 1
      }

    }
  }
  return answer
}

//function to compare arrays
function areEqual(arr1,arr2){
  if (arr1.length != arr2.length) return false
  for (var i in arr1)
    if (arr1[i] != arr2[i]) return false
  return true
}

</script>

这个JavaScript示例是线性时间吗

<script>

function matchT(t,s){
  var tMap = [], answer = []

  //map the character count in t
  for (var i=0; i<t.length; i++){
    var chr = t.charCodeAt(i)

    if (tMap[chr]) tMap[chr]++
    else tMap[chr] = 1
  }

  //traverse string
  for (var i=0; i<s.length; i++){
    if (tMap[s.charCodeAt(i)]){
      var start = i, j = i + 1, tmp = []

      tmp[s.charCodeAt(i)] = 1

      while (tMap[s.charCodeAt(j)]){
        var chr = s.charCodeAt(j++)

        if (tmp[chr]){
          if (tMap[chr] > tmp[chr]) tmp[chr]++
          else break
        }
        else tmp[chr] = 1
      }

      if (areEqual (tmp,tMap)){
        answer.push(start)
        i = j - 1
      }

    }
  }
  return answer
}

//function to compare arrays
function areEqual(arr1,arr2){
  if (arr1.length != arr2.length) return false
  for (var i in arr1)
    if (arr1[i] != arr2[i]) return false
  return true
}

</script>

让我们考虑给定的例子, 字符串S:abcdef 字符串T:efd

创建一个HashSet,该HashSet由子字符串T中的字符组成

为子字符串T:1e1f1d生成标签。每个字符的出现次数+字符本身,可以使用类似计数排序的技术来完成

现在我们必须为子字符串长度的输入生成标签

让我们从第一个位置开始,它具有字符a。由于它不存在,我们不会创建任何子字符串并移动到下一个字符b。类似地,在字符c处,然后在d处停止

由于HashSet中存在d,所以每次字符出现时,都要开始生成子字符串长度的标签。我们可以在不同的函数中执行此操作,以避免清除计数数组。这样可以将复杂性从Om*n降低到Om+n。如果在任何一点上输入字符串不包含子字符串T,我们可以从下一个位置开始生成标签,因为直到中断发生的位置不能是字谜的一部分

因此,通过生成标签,我们可以解决线性Om+n时间复杂度的问题

m:输入字符串的长度,
n:子字符串的长度。

让我们考虑给定的字符串。 实例 字符串S:abcdef 字符串T:efd

创建一个HashSet,该HashSet由子字符串T中的字符组成

为子字符串T:1e1f1d生成标签。每个字符的出现次数+字符本身,可以使用类似计数排序的技术来完成

现在我们必须为子字符串长度的输入生成标签

让我们从第一个位置开始,它具有字符a。由于它不存在,我们不会创建任何子字符串并移动到下一个字符b。类似地,在字符c处,然后在d处停止

由于HashSet中存在d,所以每次字符出现时,都要开始生成子字符串长度的标签。我们可以在不同的函数中执行此操作,以避免清除计数数组。这样可以将复杂性从Om*n降低到Om+n。如果在任何一点上输入字符串不包含子字符串T,我们可以从下一个位置开始生成标签,因为直到中断发生的位置不能是字谜的一部分

因此,通过生成标签,我们可以解决线性Om+n时间复杂度的问题

m:输入字符串的长度,
n:子字符串的长度。

下面的代码我用于GFG中的模式搜索问题,它在所有测试用例中都被接受,并且在线性时间内工作

//{驱动程序代码启动 导入java.util.*; 类实现\u strstr { 公共静态字符串参数[] { 扫描仪sc=新的扫描系统.in; int t=sc.nextInt; sc.nextLine; whilet>0 { 字符串行=sc.nextLine; 字符串a=line.split[0]; 字符串b=line.split[1]; GfG=新的GfG; System.out.printlng.strstra,b; t-; } } }//}驱动程序代码结束 GfG类 { //函数来定位字符串s中字符串x的出现位置。 int STRSTRSTR字符串a、字符串d { ifa.equals&&d.equals返回0; ifa.length==1&&d.length==1&&a.equalsd返回0; 如果d.length==1&&a.charAta.length-1==d.charAt0,则返回a.length-1; int t=0; int pl=-1; 布尔b=假; int fl=-1;
forint i=0;i下面的代码我用于GFG中的模式搜索问题,它在所有测试用例中都被接受,并且在线性时间内工作

//{驱动程序代码启动 导入java.util.*; 类实现\u strstr { 公共静态字符串参数[] { 扫描仪sc=新的扫描系统.in; int t=sc.nextInt; sc.nextLine; whilet>0 { 字符串行=sc.nextLine; 字符串a=line.split[0]; 字符串b=line.split[1]; GfG=新的GfG; System.out.printlng.strstra,b; t-; } } }//}驱动程序代码结束 GfG类 { //函数来定位字符串s中字符串x的出现位置。 int STRSTRSTR字符串a、字符串d { ifa.equals&&d.equals返回0; ifa.length==1&&d.length==1&&a.equalsd返回0; 如果d.length==1&&a.charAta.length-1==d.charAt0,则返回a.length-1; int t=0; int pl=-1; 布尔b=假; int fl=-1;
forint i=0;在此上下文中,请小心使用哈希映射。在典型哈希映射实现中查找的最坏情况时间与映射中的元素数呈线性关系。所有键都可以哈希到同一个bucket。在此上下文中,请小心使用哈希映射。在典型哈希映射实现中查找的最坏情况时间与num呈线性关系映射中的元素数。所有键可能散列到同一个存储桶。您好。请尝试格式化您的代码,并尝试解释其工作原理。帮助人们更好地理解您的解决方案。您好。请尝试格式化您的代码,并尝试解释其工作原理。帮助人们更好地理解您的解决方案。
console.log(matchT("edf","ghjfedabcddef"))
[3, 10]