使用LINQ检查一个字符串的字符是否包含在另一个字符串中

3
我正在使用C#在命令行中制作一个Scrabble游戏。玩家必须输入像下面这样的一些单词:
单词 得分
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();

但是它并不按照我的意愿工作。这段代码无法选择任何单词。我知道原因是因为在任何单词中都不存在。 我的问题是,有没有一种方法可以检查字符串中的字符是否包含在单词中,而不必循环遍历字符。
注意:每个字母在解决方案中最多只能使用一次。
如果我计算结果,它必须是或。(我必须编写逻辑)
附注:我正在玩这个游戏:www.codingame.com/ide/puzzle/scrabble

你能给我们举个计算的例子吗? - Orel Eraki
2
你应该检查它们的Intersection是否为空。 - Sehnsucht
3个回答

3

这种方法的性能并不高,但至少能解决问题。注意,我仅出于简单起见使用了字典 (而且我不明白为什么会有像"potsie"这样重复的单词,我从未玩过Scrabble),如果你遵循这段代码,你也可以使用Tuple列表。

编辑:我根据OP的新评论做出了修改。

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);
        }       

    }
}

2
(只是挑剔一下)你其实不需要使用 lambda;使用方法组就足够了:Any(letters.Contains) - Sehnsucht
谢谢!可惜在dotnetfiddle上我没有ReSharper :D - Andrés Robinet
不,它没有给我想要的结果。你的代码的结果是 have。结果必须是 postie 或者 potsie - H. Pauwelyn
所以你可以有重复的单词吗?对于重复的条目,你要如何处理积分?是将它们相加吗? - Andrés Robinet
1
我现在明白了,没有重复项,你的 postie != postie... 你可能想要匹配输入流中大部分字符的单词,让我来修复一下 :P - Andrés Robinet
是的,确实如此。 :) - H. Pauwelyn

1
这里的问题是Contains只检查一个字符串是否包含另一个字符串;它并没有检查是否包含所有这些字符。你需要使用HashSet<char>替换字典中的每个字符串,并执行集合比较,如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) 吗? - H. Pauwelyn
我认为他试图计算一种“某种得分”,除了已经完全匹配的单词... - Andrés Robinet
问题在于筛选哪些单词实际上可以被玩。他使用了 Contains 进行过滤,但这会排除所有单词,因为它不检查字母计数/组合,而是检查子字符串。 - JamesFaix
是的,我完全理解。我第一次看他的帖子时误读了(哈哈)。 - Andrés Robinet

1

由于字母可能会重复出现,我认为您需要像这样的东西(当然这并不是非常高效的,但是纯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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接