【问题标题】:Check if chars of a string contains in another string with LINQ使用 LINQ 检查字符串的字符是否包含在另一个字符串中
【发布时间】:2020-12-16 22:07:50
【问题描述】:

我正在使用 C# 在命令行中制作拼字游戏。玩家必须输入一些单词,如下表:

Word Points
some 6
first 8
potsie 8
day 7
could 8
postie 8
from 9
have 10
back 12
this 7

玩家得到的字母是这样的:

sopitez

这个值是一个字符串。我会检查这些字母是否包含在单词中。为此,我尝试了以下代码:

String highst = (from word
                 in words 
                 where word.Contains(letters)
                 orderby points descending
                 select word).First();

但是我怎么做都行不通。此代码不会选择任何单词。我知道原因是因为sopitez 不包含任何单词。

我现在的问题是有没有办法检查字符串letters 中的字符是否包含在循环字符的单词中。

注意:每个字母在解中最多只能使用一次。

如果我计算结果,它必须是potsiepostie(我必须为此编写逻辑)

PS:我在玩这个游戏:www.codingame.com/ide/puzzle/scrabble

【问题讨论】:

  • 你能给我们一个计算的例子吗?
  • 你应该检查他们的Intersection是否为空

标签: c# string linq


【解决方案1】:

这根本不会有性能,但至少它可以解决问题。请注意,为了简单起见,我使用了字典(我也不明白为什么你会重复“potsie”之类的词,我从来没有玩过拼字游戏)。 你可以如果您遵循此代码,请使用元组列表

编辑:我根据 OP 的新 cmets 更改了这个

using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {

        var letters = new HashSet<char>("sopitez");

        var wordsMap = new Dictionary<string, int>()
        {
            {"some", 6}, {"first", 8}, {"potsie", 8}, {"postie", 8}, {"day", 7},
            {"could", 8}, {"from", 9}, {"have", 10}, {"back", 12},
            {"this", 7}
        };

        var highest = wordsMap
            .Select(kvp => {
                var word = kvp.Key;
                var points = kvp.Value;
                var matchCount = kvp.Key.Sum(c => letters.Contains(c) ? 1 : 0);
                return new {
                    Word = word,
                    Points = points,
                    MatchCount = matchCount,
                    FullMatch = matchCount == word.Length,
                    EstimatedScore = points * matchCount /(double) word.Length // This can vary... it's just my guess for an "Estiamted score"
                };
            })
            .OrderByDescending(x => x.FullMatch)
            .ThenByDescending(x => x.EstimatedScore);


        foreach (var anon in highest)
        {
            Console.WriteLine("{0}", anon);
        }       

    }
}

【讨论】:

  • (只是吹毛求疵)你真的不需要 lambda ;使用方法组就足够了:Any(letters.Contains)
  • 谢谢!可惜我在 dotnetfiddle 中没有 ReSharper:D
  • 不,它不会给我我想要的结果。您的代码的结果是have。结果必须是postiepotsie
  • 所以你可以有重复的单词?你如何处理重复条目的积分,将它们相加?
  • 我现在看到了,没有重复项,你 postie != postie... 你可能想要匹配输入流中大多数字符的单词,让我来修复:P
【解决方案2】:

这里的问题是Contains 检查一个字符串是否包含另一个字符串;它没有检查它是否包含所有这些字符。 您需要用HashSet&lt;char&gt; 替换字典中的每个字符串,并执行IsSubsetIsSuperset 等集合比较以确定字母是否匹配。

这是你正在做的事情:

string a= "Hello";
string b= "elHlo";
bool doesContain = b.Contains(a); //This returns false

这是你需要做的:

var setA = new HashSet<char>(a);
var setB = new HashSet<char>(b);    
bool isSubset = a.IsSubsetOf(b); //This returns true

更新 实际上,这是错误的,因为集合删除了重复的元素。但本质上你是在滥用Contains。您将需要一些更复杂的序列比较,以允许重复的字母。

更新2 你需要这个来比较单词/字母:

//Compares counts of each letter in word and tiles
bool WordCanBeMadeFromLetters(string word, string tileLetters) {

    var tileLetterCounts = GetLetterCounts(tileLetters);
    var wordLetterCounts = GetLetterCounts(word);

    return wordLetterCounts.All(letter =>
        tileLetterCounts.ContainsKey(letter.Key)
        && tileLetterCounts[letter.Key] >= letter.Value);
}

//Gets dictionary of letter/# of letter in word
Dictionary<char, int> GetLetterCounts(string word){
    return word
        .GroupBy(c => c)
        .ToDictionary(
            grp => grp.Key,
            grp => grp.Count());
}

所以您的原始示例可能如下所示:

String highst = (from word
             in words 
             where WordCanBeMadeFromLetters(word, letters)
             orderby points descending
             select word).First();

【讨论】:

  • 必须是setA.IsSubsetOf(setB) 而不是a.IsSubsetOf(b)
  • 我以为他是在计算一个“排序分数”,除了完全匹配的单词......
  • 问题在于过滤哪些单词可以实际播放。他使用Contains 过滤,排除了他的所有单词,因为它不检查字母计数/组合,而是检查子字符串。
  • 是的,完全明白。我只是第一次看错了他的帖子(lol)
【解决方案3】:

由于字母可以重复,我认为你需要这样的东西(当然这不是很有效,但纯 LINQ):

var letters = "sopitezwss";

var words = new Dictionary<string, int>() {
    {"some", 6}, {"first", 8}, {"potsie", 8}, {"day", 7},
    {"could", 8}, {"from", 9}, {"have", 10}, {"back", 12},
    {"this", 7}, {"postie", 8}, {"swiss", 15}
};

var highest = (from word
    in words
    where word.Key.GroupBy(c => c).All(c => letters.Count(l => l == c.Key) >= c.Count())
    orderby word.Value descending
    select word);

【讨论】:

    猜你喜欢
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-14
    • 2021-11-26
    • 1970-01-01
    • 2013-10-29
    • 2021-05-06
    相关资源
    最近更新 更多