创建给定单词的所有字谜的惰性流

时间:2014-06-17 00:28:42

标签: java algorithm java-8 anagram

我试图编写代码来创建给定单词的所有字谜的惰性流。我最初使用这段代码:

public static Stream<WordSequence> anagram(Stream<WordSequence> data, Object[] parameters) {
    return data.unordered().flatMap(WordSequence.forEachWord(Functions::allAnagrams)).distinct();
}

private static Stream<Word> allAnagrams(Word data) {
    if (data.length() <= 1)
        return Stream.of(data);
    Stream<Word> ret = Stream.empty();
    for (int i = 0; i < data.length(); i++) {
        char ch = data.charAt(i);
        String rest = new StringBuilder(data).deleteCharAt(i).toString();
        ret = Stream.concat(ret, allAnagrams(new Word(rest)).map(word -> new Word(ch + word.toString()))).unordered();
    }
    return ret;
}

(我使用自己的WordSequenceWord课程。)

我意识到这不是很有效,因为它只是连接一堆空元素和单元素流,并且它还会在返回它们的流之前计算所有字符串。我在Core Java中找到了这个很棒的算法:

StringBuilder b = new StringBuilder(word);
for (int i = b.length() - 1; i > 0; i--)
    if (b.charAt(i - 1) < b.charAt(i)) {
        int j = b.length() - 1;
        while (b.charAt(i - 1) > b.charAt(j))
            j--;
        swap(b, i - 1, j);
        reverse(b, i);
        return new Word(b.toString());
    }
return new Word(b.reverse().toString());

如果你用一个单词调用它,它将返回单词的所有字符序列中的下一个单词。

我按如下方式实施:

public static Stream<WordSequence> anagram(Stream<WordSequence> data, Object[] parameters) {
    class AnagramIterator implements Iterator<Word> {
        private final Word start;
        private Word current;
        private boolean done;

        AnagramIterator(Word start) {
            current = this.start = start;
        }

        @Override
        public boolean hasNext() {
            return !done;
        }

        @Override
        public Word next() {
            if (done)
                throw new NoSuchElementException();
            StringBuilder b = new StringBuilder(current);
            for (int i = b.length() - 1; i > 0; i--)
                if (b.charAt(i - 1) < b.charAt(i)) {
                    int j = b.length() - 1;
                    while (b.charAt(i - 1) > b.charAt(j))
                        j--;
                    swap(b, i - 1, j);
                    reverse(b, i);
                    current = new Word(b.toString());
                    done = current.equals(start);
                    return current;
                }
            current = new Word(b.reverse().toString());
            done = current.equals(start);
            return current;
        }

        private void swap(StringBuilder b, int i, int j) {
            char tmp = b.charAt(i);
            b.setCharAt(i, b.charAt(j));
            b.setCharAt(j, tmp);
        }

        private void reverse(StringBuilder b, int i) {
            int j = b.length() - 1;
            while (i < j) {
                swap(b, i, j);
                i++;
                j--;
            }
        }
    }
    return data.flatMap(WordSequence.forEachWord(w -> StreamSupport.stream(
            Spliterators.spliteratorUnknownSize(
                    new AnagramIterator(w),
                    Spliterator.DISTINCT + Spliterator.IMMUTABLE + Spliterator.NONNULL),
            false)));
}

但是,该算法存在问题。如果你给它一个单词以双字母结尾然后另一个字母,其中双字母值在数字上小于单个字母,例如&#34; ees&#34;,你得到这个字母序列:

ees
ese
ees
and that repeats infinitely

该序列不包含&#34;请参阅&#34;。

我该怎么做?

我的代码是on GitHub

1 个答案:

答案 0 :(得分:2)

我想到了算法正在做什么,并且有一丝洞察力。给定字符串&#34; ese&#34;,这就是算法的作用:

  • 查找i,在这种情况下指向s。
  • 查找j,指向e。
  • 交换i - 1j,交换两个e。
  • i开始反转字符串,交换s和e。

我们希望它做的是j指向s,这将使它交换第一个e和s。那么我们如何修改算法来实现呢?

嗯,这里找到j

的作用
  • 首先将j指向最后一个e。
  • i - 1,即e,不大于j,这是另一个e,因此j指向最后一个e。

以下是我的洞察力:改变比较大于&#34;大于&#34;到&#34;大于或等于&#34;。我改变了,它似乎有效!