String 给定一组单词,您如何识别;";帮助您从原始列表中获得最大完整单词数的字母集?

String 给定一组单词,您如何识别;";帮助您从原始列表中获得最大完整单词数的字母集?,string,algorithm,graph,String,Algorithm,Graph,例如: n=9 words={自行车、轮胎、燃油、摩托车手、过滤器、三轮车} output={B,T,I,K,E,F,U,L,R} (输出的顺序并不重要。需要注意的是,对于像FOO这样的单词,不能使用F,O作为字母表,但总是需要F,O,O。类似的字母表是分开处理的) 解决这个问题最有效的算法是什么? 我在考虑使用每个字符的频率,但这似乎没有多大帮助 编辑:已为已编辑的问题更新此选项。有关详细信息,请参阅 根据评论,人们必须假定(或至少考虑可能性)这实际上是一个NP完全问题。因此,除非有人证明或反

例如:

n=9

words={自行车、轮胎、燃油、摩托车手、过滤器、三轮车}

output={B,T,I,K,E,F,U,L,R}

(输出的顺序并不重要。需要注意的是,对于像FOO这样的单词,不能使用F,O作为字母表,但总是需要F,O,O。类似的字母表是分开处理的)

解决这个问题最有效的算法是什么?
我在考虑使用每个字符的频率,但这似乎没有多大帮助

编辑:已为已编辑的问题更新此选项。有关详细信息,请参阅

根据评论,人们必须假定(或至少考虑可能性)这实际上是一个NP完全问题。因此,除非有人证明或反驳这个问题的实际复杂性,这里有一个蛮力解决方案,至少应该计算正确的输出

编辑2.0:As,它确实是NP完全的

它使用一些从所有单词的初始集合计算一定数量的字母的所有组合。由于存在
n^k
字母组合
k
(给定
n
字母的初始集合),这显然不是多项式时间解意义上的“有效”解决方案,但尚不清楚是否存在这样的解

为了根据编辑问题中提到的观点(即字母必须与单词中的字母一样频繁地出现在结果列表中)验证输出,我使用了一个输入示例,其中字母重复:

"BIKE", "BIKER", "TRIKE", "BEER", DEER", "SEED", "FEED"
对于此输入,程序将打印

0 letters: [], created words: []
1 letters: [B], created words: []
2 letters: [B, B], created words: []
3 letters: [B, B, B], created words: []
4 letters: [B, E, E, R], created words: [BEER]
5 letters: [B, D, E, E, R], created words: [BEER, DEER]
6 letters: [B, D, E, E, F, R], created words: [BEER, DEER, FEED]
7 letters: [B, D, E, E, F, R, S], created words: [BEER, DEER, SEED, FEED]
8 letters: [B, D, E, E, F, I, K, R], created words: [BIKE, BIKER, BEER, DEER, FEED]
也许它可以被认为是有帮助的,也许是其他人的起点或基石

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;

public class MaximizeWords
{
    public static void main(String[] args)
    {
        List<String> words = Arrays.asList(
            "BIKE",
            "BIKER",
            "TRIKE",

            "BEER",
            "DEER",
            "SEED",
            "FEED"
        );

        List<Character> allLetters = 
            new ArrayList<Character>(allLettersOf(words));
        for (int n=0; n<=8; n++)
        {
            CombinationIterable<Character> combinations =
                new CombinationIterable<Character>(n, allLetters);

            List<Solution> solutions = new ArrayList<Solution>();
            for (List<Character> combination : combinations)
            {
                Collections.sort(combination);
                Solution solution = new Solution(words, combination);
                solutions.add(solution);
            }
            Solution bestSolution = Collections.max(solutions, 
                new Comparator<Solution>()
            {
                @Override
                public int compare(Solution s0, Solution s1)
                {
                    return Integer.compare(
                        s0.createdWords.size(), s1.createdWords.size());
                }
            });
            System.out.println(bestSolution);
        }
    }

    static class Solution
    {
        List<Character> letters;
        List<String> createdWords;

        public Solution(List<String> words, List<Character> letters)
        {
            this.letters = letters;
            this.createdWords = computeCreatedWords(words, letters);
        }

        @Override
        public String toString()
        {
            return letters.size() + " letters: " + letters
                + ", created words: " + createdWords;
        }
    }

    private static List<String> computeCreatedWords(
        List<String> words, List<Character> letters)
    {
        List<String> createdWords = new ArrayList<String>();
        for (String word : words)
        {
            if (creates(letters, word))
            {
                createdWords.add(word);
            }
        }
        return createdWords;
    }

    private static boolean creates(List<Character> letters, String word)
    {
        List<Character> copy = new ArrayList<Character>(letters);
        for (int i=0; i<word.length(); i++)
        {
            Character c = Character.valueOf(word.charAt(i));
            if (!copy.remove(c))
            {
                return false;
            }
        }
        return true;
    }


    private static List<Character> lettersOf(String word)
    {
        List<Character> letters = new ArrayList<Character>();
        for (int i=0; i<word.length(); i++)
        {
            letters.add(Character.valueOf(word.charAt(i)));
        }
        return letters;
    }

    private static Set<Character> allLettersOf(Iterable<String> words)
    {
        Set<Character> letters = new TreeSet<Character>();
        for (String word : words)
        {
            letters.addAll(lettersOf(word));
        }
        return letters;
    }
}







//=============================================================================
// These classes are taken from https://github.com/javagl/Combinatorics


/**
 * A class providing an iterator over all combinations of a certain number
 * of elements of a given set. For a set S with n = |S|, there are are n^k 
 * combinations of k elements of the set. This is the number of possible
 * samples when doing sampling with replacement. Example:<br />
 * <pre>
 * S = { A,B,C }, n = |S| = 3
 * k = 2 
 * m = n^k = 9
 * 
 * Combinations:
 * [A, A]
 * [A, B]
 * [A, C]
 * [B, A]
 * [B, B]
 * [B, C]
 * [C, A]
 * [C, B]
 * [C, C]
 * </pre>
 *  
 * @param <T> The type of the elements
 */
final class CombinationIterable<T> implements Iterable<List<T>>
{
    /**
     * The input elements
     */
    private final List<T> input;

    /**
     * The sample size
     */
    private final int sampleSize;

    /**
     * The total number of elements that the iterator will provide
     */
    private final int numElements;

    /**
     * Creates an iterable over all multisets of 
     * 'sampleSize' elements of the given array.
     *  
     * @param sampleSize The sample size
     * @param input The input elements
     */
    public CombinationIterable(int sampleSize, List<T> input)
    {
        this.sampleSize = sampleSize;
        this.input = input;
        numElements = (int) Math.pow(input.size(), sampleSize);
    }

    @Override
    public Iterator<List<T>> iterator()
    {
        return new Iterator<List<T>>()
        {
            /**
             * The element counter
             */
            private int current = 0;

            /**
             * The indices of the elements that are currently chosen
             */
            private final int chosen[] = new int[sampleSize];

            @Override
            public boolean hasNext()
            {
                return current < numElements;
            }

            @Override
            public List<T> next()
            {
                if (!hasNext())
                {
                    throw new NoSuchElementException("No more elements");
                }

                List<T> result = new ArrayList<T>(sampleSize);
                for (int i = 0; i < sampleSize; i++)
                {
                    result.add(input.get(chosen[i]));
                }
                increase();
                current++;
                return result;
            }

            /**
             * Increases the k-ary representation of the selection of 
             * elements by one.
             */
            private void increase()
            {
                // The array of 'chosen' elements for a set of size n 
                // effectively is a number represented in k-ary form, 
                // and thus, this method does nothing else than count. 
                // For example, when choosing 2 elements of a set with 
                // n=10, the contents of 'chosen' would represent all
                // values 
                // 00, 01, 02,... 09,
                // 10, 11, 12,... 19,
                // ...
                // 90, 91, 92, ...99
                // with each digit indicating the index of the element
                // of the input array that should be placed at the
                // respective position of the output array.
                int index = chosen.length - 1;
                while (index >= 0)
                {
                    if (chosen[index] < input.size() - 1)
                    {
                        chosen[index]++;
                        return;
                    }
                    chosen[index] = 0;
                    index--;
                }
            }

            @Override
            public void remove()
            {
                throw new UnsupportedOperationException(
                    "May not remove elements from a combination");
            }
        };
    }
}

/**
 * Utility methods used in the combinatorics package
 */
class Utils
{
    /**
     * Utility method for computing the factorial n! of a number n.
     * The factorial of a number n is n*(n-1)*(n-2)*...*1, or more
     * formally:<br />
     * 0! = 1 <br />
     * 1! = 1 <br />
     * n! = n*(n-1)!<br />
     *
     * @param n The number of which the factorial should be computed
     * @return The factorial, i.e. n!
     */
    public static BigInteger factorial(int n)
    {
        BigInteger f = BigInteger.ONE;
        for (int i = 2; i <= n; i++)
        {
            f = f.multiply(BigInteger.valueOf(i));
        }
        return f;
    }    
    /**
     * A magic utility method that happens to return the number of
     * bits that are set to '1' in the given number.
     *  
     * @param n The number whose bits should be counted
     * @return The number of bits that are '1' in n
     */
    public static int countBits(int n)
    {
        int m = n - ((n >> 1) & 033333333333) - ((n >> 2) & 011111111111);
        return ((m + (m >> 3)) & 030707070707) % 63;
    }

    /**
     * Add all elements from the given iterable into the given collection
     * 
     * @param <T> A type that is related to the elements 
     * @param iterable The iterable
     * @param collection The collection
     */
    public static <T> void addAll(
        Iterable<? extends T> iterable, Collection<? super T> collection)
    {
        for (T t : iterable)
        {
            collection.add(t);
        }
    }

    /**
     * Returns all elements from the given iterable as a list
     * 
     * @param <T> A type that is related to the elements 
     * @param iterable The iterable
     * @return The list
     */
    public static <T> List<T> asList(Iterable<? extends T> iterable)
    {
        List<T> list = new ArrayList<T>();
        addAll(iterable, list);
        return list;
    }

    /**
     * Returns all elements from the given iterable as a set
     * 
     * @param <T> A type that is related to the elements 
     * @param iterable The iterable
     * @return The set
     */
    public static <T> Set<T> asSet(Iterable<? extends T> iterable)
    {
        Set<T> set = new LinkedHashSet<T>();
        addAll(iterable, set);
        return set;
    }

    /**
     * Private constructor to prevent instantiation
     */
    private Utils()
    {

    }
}
import java.math.biginger;
导入java.util.ArrayList;
导入java.util.array;
导入java.util.Collection;
导入java.util.Collections;
导入java.util.Comparator;
导入java.util.Iterator;
导入java.util.LinkedHashSet;
导入java.util.List;
导入java.util.NoSuchElementException;
导入java.util.Set;
导入java.util.TreeSet;
公共类最大化
{
公共静态void main(字符串[]args)
{
List words=Arrays.asList(
“自行车”,
“摩托车手”,
“三轮车”,
“啤酒”,
“鹿”,
“种子”,
“饲料”
);
列出所有字母=
新ArrayList(所有文字);
对于(int n=0;n>2)和0111111);
返回((m+(m>>3))&030707)%63;
}
/**
*将给定iterable中的所有元素添加到给定集合中
* 
*@param与元素相关的类型
*@param iterable这个iterable
*@param collection该集合
*/
公共静态void addAll(

Iterable用一种简单的方法,你可以做到以下几点:我正在使用C#

结果

words = {"Stupid","Stubborn","sun","safe"}, 
then the result would be S,T,U,P,I,D,B,O,R,N,A,F,E and count is 13
如果输入是

words = ['bike', 'tire', 'fuel', 'biker', 'filter', 'trike']


def next_letter(words):
    """ find the next letter from the most common letter in the words """
    num_words = {}
    max_words = 0
    next = None
    for word in words:
        if len(word) == 1:        # if we can complete this word 
            return word[0]        # with one letter, do it!
        for letter in word:
            n = num_words.get(letter, 0) + 1    # tally the number of words
            num_words[letter] = n               # that use this letter
            if n > max_words:                   # a new maximum?
                max_words = n                   # use it
                next = letter
    return next


output = ''
while words:
    letter = next_letter(words)   # get the next letter
    if not letter: break          # reached the end? exit
    output += letter              # keep track of the letters
    # remove the selected letter from every word and try again
    words = [word.replace(letter, '', 1) if letter in word else word for word in words]

print '{', ','.join(output), '}'
换句话说:你的问题是找到组成单词集所需的最小字母集,这意味着删除单词中所有重复的字符


这是一个实用的

这是一个用Python编写的版本。基本算法是:找到最常见的字母并使用它;从每个单词中删除该字母并重复。在这个过程中,如果我们只需要一个字母就可以完成一个单词,请先使用该字母

这种方法的优点是,随着
n
的增加,它“向前看”以最大化字数

import itertools

n = 9
words = ['bike', 'tire', 'fuel', 'biker', 'filter', 'trike']

# preparation: get the union of all letters in all words, including duplicate letters
all_letters = ''
for word in words:
    a = all_letters[:]
    for letter in word:
        if letter in a:
            a = a.replace(letter, '', 1)
        else:
            all_letters += letter

# helper function: find if a word with duplicate letters in a combination
def word_in_combo(word, combo):
    letters = list(combo)
    for letter in word:
        if letter not in letters:
            return False
        letters.remove(letter)
    return True

# algorithm: find all words for each combination of n letters
matches = {}
max_matched = 0
for combo in itertools.combinations(all_letters, n):
    matched = 0
    for word in words:
        if word_in_combo(word, combo):
            matched += 1
    matches[combo] = matched
    if matched > max_matched:
        max_matched = matched

# print the best combinations and the matching words
if max_matched == 0:
    print "No combinations for %d letters" % n
else:
    for combo in matches:
        if matches[combo] == max_matched:
            print combo, ':',
            for word in words:
                if word_in_combo(word, combo):
                    print word,
            print
此程序的输出如下所示:

{e,i,r,t,k,b,f,l,u}


终于有时间查了一下,然后:

这个是的一个变体-它实际上是。正如我所怀疑的,它是


因此,总而言之,@Marco13给出了一个最好的答案(渐近)你可以这样做。它可能会被优化,还有其他技巧,但基本上,这是最好的。这里有另一个Python版本,它只使用十行代码作为核心算法。它显示了所有可能的字母组合,以获得最大完整单词数。它还处理重复的字母(如
FOO

对于
n=4
,输出为:

('i', 'k', 'e', 't', 'r') : tire trike
('b', 'i', 'k', 'e', 'r') : bike biker
对于
n=5
,输出为:

('i', 'k', 'e', 't', 'r') : tire trike
('b', 'i', 'k', 'e', 'r') : bike biker

这感觉像是封面问题的一个变种,但从另一个角度来看prepective@shapiro.yaacov事实上,这有点NP完全的味道。我想知道谁敢在没有正式的同行评议证据证明他找到了“最有效的解决方案”的情况下发布答案对于这一个…@Marco13-我有一些想法,还有一些疑难案例的例子(比如
{am,an,at,ac,awe,ark,owl,wol,low}
,其中
a
非常流行,但是没有其他字母就没有用处,但是
o,w,l
组成三个单词),但算法很复杂……这实际上比最大覆盖率差得多;即使限制为两个字母的单词,这也至少和最密集的子图一样困难,而最密集的子图比查找小团体更困难。我们能提供具有“好”结果的算法吗?(放松“最有效”…)我还没有看过代码,但这是一个很好的开始!我刚刚运行了代码,我观察到的一件事是它多次使用字母。在理想情况下,这将是一个限制。你只能使用一个字母一次。如果需要在一个单词中多次使用字母表,那么需要以不同的方式处理。Wi我现在将正确地检查代码:)谢谢这个精彩的开始!好吧,这还不清楚-我在这里想了一个。所以如果我理解正确:给一个像
FOO
这样的词,你不能用
F,O
作为字母表,但总是需要
F,O,O
,对吗?也许你可以用这样的信息编辑这个问题…是的,我同意,我会添加这些信息!是吗不需要从字母计算单词?或者只计算字母
('e', 'f', 'u', 'l') : fuel
('i', 'e', 't', 'r') : tire
('b', 'i', 'k', 'e') : bike
('i', 'k', 'e', 't', 'r') : tire trike
('b', 'i', 'k', 'e', 'r') : bike biker