【问题标题】:How to determine if one string starts with what another ends with如何确定一个字符串是否以另一个字符串结尾
【发布时间】:2019-02-09 13:47:05
【问题描述】:

我有一个短语列表:

[
  "according to all known laws of aviation",
  "time flies when you're having fun..."
  ...
]

我想查找此列表中与输入字符串结尾匹配的所有短语。例如:

getNext("did you know that time flies w")

应该在列表中执行搜索以匹配以did you know that time flies w 结尾的任何字符串。例如,它应该返回一个至少包含 time flies when you're having fun 的列表,因为它以 time flies w 结尾,这是该短语的开头。

我想不出任何合理的方法来做到这一点。

我唯一能想到的就是遍历输入中的每个字符并检查它是否与短语的开头匹配。如果是,则增加一个计数器。如果它在循环结束时大于 1(或其他数字),则在短语到另一个列表时,以及匹配的数量。从那里我可以对列表进行排序,首先给出最接近的匹配,最后给出不太接近的匹配。

肯定有更好的方法来做到这一点?

有关背景知识,您可以查看this Repl.it projectthis very similar Python question

【问题讨论】:

  • 但是time flies when you're having fun 没有结束 time flies w
  • 你如何确定一个短语“以”结尾?因为在您列出的示例中,它是一个以另一个字符串开头的字符串。如果您输入 one two time 会发生什么 - 您还应该返回 time flies when you're having fun 吗?
  • @Pointy 它开始time flies w。而字符串time flies w 在技术上以整个短语结束。所以我有点困惑
  • @Pointy 我猜你弄错了 "检查是否有任何行以 HTML 页面中的文本框结尾" 我们正在寻找 endOf(textbox) === startOf(textfile.line[i]) time flies when you're having fun 在文本文件中,time flies w 是输入(在文本框中)
  • @DavidWheatley Python 问题有一个例外的答案,它完全符合您的要求

标签: javascript arrays string


【解决方案1】:

我在这里回答感到非常沮丧,没有什么比蛮力更好的了......我真的很喜欢一个更聪明的方式来解决它,但仍然希望有人会这样做。

但是考虑到您的应用程序,您必须在 jojonas 的算法上添加一些优化。您可能会在每次击键时执行此检查,并且由于它是自动完成的,因此输入的长度可能会增长得很快。

第一个优化是将输入切割成检查字符串的长度。 给定长度为 8 的输入和长度为 3 的要比较的字符串,前 5 次迭代不可能返回匹配项

input:   "aaaaaaaa" (8)
compare:      "aaa" (3)
               ^- first possible match for compare.startsWith()

这可以很容易地通过将我们的迭代器初始化到max(input.length - compare.length, 0)来完成。

对该算法的第二个优化在于在input 的其余部分中搜索compare 的第一个字母的第一个索引。无需遍历每个字符,只要它不是compare 的第一个字符,我们就可以确定compare.startsWith 将返回false。然后我们可以重复,直到找到正确的余数,再一次在整个过程中删除相当多的循环。

const phrases = ["according to all known laws of aviation", "a blessing in disguise", "a bunch", "a dime a dozen", "beat around the bush", "better late than never", "bite the bullet", "break a leg", "call it a day", "cut somebody some slack", "cutting corners", "dead tired", "easy does it", "excuse me", "get it out of my system", "get it out of your system", "get out of hand", "get something out of my system", "get something out of your system", "get your act together"];


inp.oninput = e => {
  const overlaps = getOverlaps(inp.value, phrases);
  makeList(overlaps);
  if(logged) console.clear();
};
let logged = false;
inp.oninput(); 
logged = true;

// "abcde", "def"
function getOverlaps(input, list) {
  return list.filter(compare => {
    // single logger, just for demo
    const log = (...args) => !logged && compare === "beat around the bush" && console.log.apply(console, args);

    // search only in possible area
    let i = Math.max(input.length - compare.length, 0); // 2
    
    log('initial i:', i);
    
    const first_letter = compare[0]; // "d"
    let remain = input.substr(i); // "cde"
    
    log('initial remain:', remain);
    
    while(i < input.length) {
      // jump to first letter
      let index = remain.indexOf(first_letter); // 1

      log('in-loop index:', index);

      if(index < 0) { // not found
        return false;
      }
      i += index; // 3
      remain = input.substr(i); // "de"
      
      log('in-loop remain:', remain);
      
      if(compare.startsWith(remain)) { // found
        return true; // =>
      }
      
      // wasn't this one, move by one char
      // (will jump to next occurence of first_letter) at next iteration
      i++;
      remain = input.substr(i);
    }
      
  });

}

function makeList(items) {
  list.innerHTML = '';
  items.forEach(e => 
    list.appendChild(
      document.createElement('li')
    ).textContent = e
  );
}
body{padding-bottom: 120px}
<input id="inp" value="according to all known laws of aviation to bring a beat a" autofocus>
<ul id="list"></ul>

【讨论】:

    【解决方案2】:

    如果您只想匹配某个字符串数组的开头,可以使用filterstartsWith 函数对。

      let test = "a ";
      const items = phrases.filter(phrase => phrase.startsWith(test));
    

    注意如果您不关心并且您的字符串都是小写的,请使用.toLowerCase() 使其匹配“I”和“i”;

      let test = "a ";
      const items = phrases.filter(phrase => phrase.toLowerCase().startsWith(test));
    

    注意:我编辑了这个实时示例以通过复选框显示“包含”,以防万一有助于说明替代方案。

    这是一个活生生的例子:

    let phrases = ["according to all known laws of aviation", "a blessing in disguise", "a bunch", "a dime a dozen", "beat around the bush", "better late than never", "bite the bullet", "break a leg", "call it a day", "cut somebody some slack", "cutting corners", "dead tired", "easy does it", "excuse me", "get it out of my system", "get it out of your system", "get out of hand", "get something out of my system", "get something out of your system", "get your act together", "give someone the benefit of the doubt", "go back to the drawing board", "good idea", "hang in there", "hit the nail on the head", "hit the sack", "how does that sound?", "i don't know", "i don't understand", "i love you", "i owe you one", "i'm fine", "i'm fine, thanks", "i'm really sorry", "i'm sorry", "it cost an arm and a leg", "it costs an arm and a leg", "it'll cost an arm and a leg", "it will cost an arm and a leg", "it's not rocket science", "i've never given it much thought", "kill two birds with one stone", "killing two birds with one stone", "let you off the hook", "let me off the hook", "look, it's", "make a long story short", "miss the boat", "never gonna give you up", "no pain,  no gain ", "on the ball ", "once upon a time ", "pull your leg ", "pulling your leg ", "pull yourself together ", "proof of concept ", "so far so good ", "so much ", "speak of the devil ", "thanks so much ", "thank you so much ", "that's the last straw ", "the best of both worlds ", "this is a test", "time flies when you're having fun", "to be honest", "to get bent out of shape", "to kill a mockingbird", "to make matters worse", "under the weather", "watch out", "we'll cross that bridge when we come to it ", "whatever floats your boat", "whatever you say ", "wrap your head around something", "you can say that again", "your guess is as good as mine", "there's no such thing as a free lunch", "throw caution to the wind", "you can't have your cake and eat it too ", "have your cake and eat it too", "judge a book by its cover ", "book by its cover", "last straw", "shut up", "be quiet", "how are you?", "never gonna give you up", "water under the bridge", "let you down", "birds and the bees", "pair of trainers", "i'd really like", "i wouldn't mind", "i could do with"];
    
    $('#mywords').on('input change', function(event) {
      let grp = $("<div></div>");
      let test = $(this).val();
      $('#testing').html(test);
      const items = phrases.filter(phrase => phrase.startsWith(test));
      $.each(items, function(index, value) {
        let rowItem = $("<div class='row-item'></div>").text(index + ". " + value);
        grp.append(rowItem);
      });
      $('#results-list').html(grp.children());
    });
    
    function dochange(event) {
      let grp = $("<div></div>");
      let test = $('#mywords').val();
      $('#testing').html(test);
      let items = [];
      if (!$('#mywords-contain').prop('checked')) {
        items = phrases.filter(phrase => phrase.toLowerCase().startsWith(test));
      } else {
        items = phrases.filter(phrase => phrase.toLowerCase().includes(test));
      }
      $.each(items, function(index, value) {
        let rowItem = $("<div class='row-item'></div>").text(index + ". " + value);
        grp.append(rowItem);
      });
      $('#results-list').html(grp.children());
    }
    
    $('#mywords-contain').on('change', dochange);
    $('#mywords').on('input change', dochange);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <label>Enter something:<input id="mywords" type="text"/></label><label>Check For contains:<input id="mywords-contain" type="checkbox"/></label><span id="testing">(empty)</span>
    <div id="result-group">Result found:
      <div id="results-list"></div>
    </div>

    【讨论】:

    • 如果您也希望包含或使用 JavaScript endsWith() 函数,我会留给您。
    • 问题要复杂得多。他们想找到以输入结尾开始的短语。例如"foo beat a" 应该找到beat around... 以及所有以a 开头的短语,例如"according to.."
    【解决方案3】:

    在对 Python 语言进行了一些研究后,我将 Python 问题的答案转换为 Javascript:

    function OverlapTest(input, compare) {
        for (var i = 0; i < input.length; i++) {
            if (compare.startsWith(input.substring(i))) {
                return true;
            }
        }
    
        return false;
    }
    

    如果字符串在开头和结尾重叠,这将返回true,如果不重叠,则返回false

    【讨论】:

    • @Kaiido 这就是我想要的...正是我说我想不出任何合理的方法来做到这一点。我希望有人能有更好更快的方法来做到这一点,但结果却让人们抱怨我。
    • @DavidWheatley - 我至少不是在“抱怨”,而是建议您改进这个问题 - 显然,如果可以,我们希望提供帮助,但实际上采取了部分链接代码和文本,如编辑将会并且确实有帮助 - 包括添加此代码和“我怎样才能使它变得更好”。
    最近更新 更多