【问题标题】:Find smallest substring containing a given set of letters in a larger string在较大的字符串中查找包含给定字母集的最小子字符串
【发布时间】:2016-03-26 15:56:54
【问题描述】:

假设你有以下字符串:

FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNT
LDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFY
FFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQ
XBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR
AMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR

我正在尝试查找包含字母 ABCDA 的最小子字符串。

我尝试了一种正则表达式方法。

console.log(str.match(/[A].*?[B].*?[C].*?[D].*?[A]/gm).sort((a, b) => a.length - b.length)[0]);

这可行,但它只查找出现 ABCDA 的字符串(按该顺序)。这意味着它不会找到字母按如下顺序出现的子字符串:BCDAA

我正在尝试更改我的正则表达式以解决此问题。如果不使用| 并输入所有不同的案例,我该怎么做?

【问题讨论】:

  • 你的预期输出是什么?
  • 不确定,但您的意思是“ABCZZAD”如果最短就可以吗?您想要包含 B、C、D 和两个 A 的字符串的最小部分吗?
  • @miguel-svq 是的 - 完全正确
  • 我不认为它是重复的,但肯定很接近并且是某种子案例。 S 是字符串,但不想找到所有元素,只是 S 元素的子集。乍一看,这似乎是一个简单的问题,但事实并非如此。 (并且看不到如何使用正则表达式有效地做到这一点)

标签: javascript regex string substring


【解决方案1】:

你不能。

让我们考虑一个特殊情况:假设您要查找的字母是A、A 和B。在您的正则表达式中的某个时刻肯定会有B。但是,B 左侧和右侧的部分是相互独立的,因此您不能从一个到另一个。在 B 右侧的子表达式中匹配了多少 As 取决于左侧部分中已经匹配的 As 的数量。这对于正则表达式是不可能的,因此您必须展开所有不同的订单,可能有很多!

另一个说明问题的流行示例是将左括号与右括号匹配。不可能编写一个正则表达式来断言在给定的字符串中,一个左括号序列后面跟着一个相同长度的右括号序列。这样做的原因是,要计算括号,您需要一个堆栈机,而不是有限状态机,但正则表达式仅限于可以使用 FSM 匹配的模式。

【讨论】:

  • 嗯......他不能“只使用正则表达式”。正则表达式是他的第一种方法,但不是问题中的条件。 ;)
  • 我知道他正在尝试使用正则表达式来做到这一点:“我正在尝试更改我的正则表达式来解决这个问题。我该怎么做[...]”
  • 当ABCDA 按顺序出现时我所拥有的工作,我认为可以更改,因此它可以解释它可能出现的所有不同顺序。但你可能是对的 :)
  • 不幸的是,我看到的唯一解决方案是生成一个包含所有不同可能顺序的正则表达式。
【解决方案2】:

也许不像使用正则表达式那么清楚(嗯,对我来说,正则表达式从来都不是很清楚:D)你可以使用蛮力(不是那么蛮力)

创建字符串的“有效”点(带有您想要的字母的点)的索引,并在其上使用双循环进行迭代,获取包含至少 5 个这些点的子字符串,检查它们是否是有效的解决方案。可能不是最有效的方法,但易于实施、理解和优化。

var haystack="UGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR";
var needle="ABCD";
var size=haystack.length;
var candidate_substring="";
var minimal_length=size;
var solutions=new Array();
var points=Array();
for(var i=0;i<size;i++){
	if(needle.indexOf(haystack[i])>-1) points.push(i);
}
var limit_i= points.length-4;
var limit_k= points.length;
for (var i=0;i<limit_i;i++){
  for(var k=i;k<limit_k;k++){
	if(points[k]-points[i]+1<=minimal_length){
		candidate_substring=haystack.substr(points[i],points[k]-points[i]+1);
		if(is_valid(candidate_substring)){
		  solutions.push(candidate_substring);
		  if(candidate_substring.length < minimal_length) minimal_length=candidate_substring.length;
		}
	}
  }
}
document.write('<p>Solution length:'+minimal_length+'<p>');
for(var i=0;i<solutions.length;i++){
  if(solutions[i].length<=minimal_length) document.write('<p>Solution:'+solutions[i]+'<p>');
}

function is_valid(candidate_substring){
  //verify we've got all characters
  for(var j=0;j<candidate_substring.length;j++){
    if(candidate_substring.indexOf(needle.charAt(j))<0) return false;
  }
  //...and verify we have two "A"
  if(candidate_substring.indexOf("A")==candidate_substring.lastIndexOf("A")) return false;
  return true;
}

【讨论】:

    【解决方案3】:

    此算法不使用正则表达式,但也找到了两种解决方案。

    var haystack = 'FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGRAMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR';
    var needle = 'ABCDA'; // the order of letters doesn't matter
    
    var letters = {};
    needle.split('').forEach(function(ch) {
      letters[ch] = letters[ch] || 0;
      letters[ch]++;
    });
    
    var shortestSubstringLength = haystack.length;
    var shortestSubstrings = []; // storage for found substrings
    
    var startingPos = 0;
    var length;
    var currentPos;
    var notFound;
    var letterKeys = Object.keys(letters); // unique leters
    do {
      lettersLeft = JSON.parse(JSON.stringify(letters)); // copy letters count object
      notFound = false;
      posStart = haystack.length;
      posEnd = 0;
      letterKeys.forEach(function(ch) {
        currentPos = startingPos;
        while (!notFound && lettersLeft[ch] > 0) {
          currentPos = haystack.indexOf(ch, currentPos);
          if (currentPos >= 0) {
            lettersLeft[ch]--;
            posStart = Math.min(currentPos, posStart);
            posEnd = Math.max(currentPos, posEnd);
            currentPos++;
          } else {
            notFound = true;
          }
        }
      });
      if (!notFound) {
        length = posEnd - posStart + 1;
        startingPos = posStart + 1; // starting position for next iteration
      }
      if (!notFound && length === shortestSubstringLength) {
        shortestSubstrings.push(haystack.substr(posStart, length));
      }
      if (!notFound && length < shortestSubstringLength) {
        shortestSubstrings = [haystack.substr(posStart, length)];
        shortestSubstringLength = length;
      }
    } while (!notFound);
    
    console.log(shortestSubstrings);
    

    【讨论】:

      【解决方案4】:

      刚刚在面试中遇到了这个问题作为编码任务,并提出了另一种解决方案,(它不像上面的那样最佳,但也许更容易理解)。

      function MinWindowSubstring(strArr) { 
      
        const N = strArr[0];
        const K = strArr[1];
      
        const letters = {};
      
        K.split('').forEach( (character) => {
          letters[character] = letters[character] ? letters[character] + 1 : 1;
        });
      
        let possibleSequencesList = [];
        
        const letterKeys = Object.keys(letters);
      
        for(let i=0; i< N.length; i++) {
      
          const char = N[i];
      
          if (new String(letterKeys).indexOf(char) !== -1) {
          
            // found a character in the string 
      
            // update all previus sequences
            possibleSequencesList.forEach((seq) => {
              if(!seq.sequenceComplete) {
                seq[char] = seq[char]-1;
                seq.lastIndex = i;
      
                // check if sequence is complete
                var sequenceComplete = true;
                letterKeys.forEach( (letter) => {
                  if(seq[letter] > 0) {
                    sequenceComplete = false;
                  }
                });
      
                seq.sequenceComplete = sequenceComplete
              }
            })
      
            // create a new sequence starting from it 
            const newSeq = {
              startPoint: i,
              lastIndex: i,
              sequenceComplete: false,
              ...letters
            }
      
            newSeq[char] = newSeq[char]-1;
      
            possibleSequencesList.push(newSeq);
          }
        }
      
        // cleanup sequences 
        let sequencesList = possibleSequencesList.filter(sequence => sequence.sequenceComplete);
        
        let output = [];
      
        let minLength = N.length;
        // find the smalles one
        sequencesList.forEach( seq => {
            if( (seq.lastIndex - seq.startPoint) < minLength) {
              minLength = seq.lastIndex - seq.startPoint;
              output = N.substring(seq.startPoint, seq.lastIndex + 1);
            }
        })
         
        return output; 
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-01-12
        • 2011-01-28
        • 1970-01-01
        • 1970-01-01
        • 2014-08-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多