簡體   English   中英

在二維字符數組中搜索出現次數

[英]Searching a 2D char array for occurrences

我希望為我得到的這個問題找到一些方向。 兩個星期以來一直在敲我的頭。 本質上,我要編寫一個函數public static int FindWords(char[][] puzzle) ,其中給定一個二維字符數組,我可以找到給定字符串集出現的次數。 鑒於:

    public static class PuzzleSolver
    {
        public static string[] DICTIONARY = {"OX","CAT","TOY","AT","DOG","CATAPULT","T"};
        static bool IsWord(string testWord)
        {
            if (DICTIONARY.Contains(testWord))
                return true;
            return false;
        }
    }

例如一個 2D 數組,如下所示:

    public static char[][] puzzle = {{'C','A','T'},
                                     {'X','Z','T'},
                                     {'Y','O','T'}};

對於以下實例,將返回 8:(AT, AT, CAT, OX, TOY, T, T, T) 因為我們將水平、垂直、對角線和反向搜索所有相同的方向。

我的方法是訪問數組中的每個字符,然后使用SearchChar函數搜索所有可能的方向......

public static int FindWords(char[][] puzzle){   
    int arrayRow    = puzzle.length;
    int arrayCol    = puzzle[0].length;
    int found       = 0;

    for(int i = 0; i < arrayRow; i++){
        for(int j = 0; j < arrayCol; j++){
            found += SearchChar(i,j);
        }
    }
    return found;
}

看起來像這樣:

public static int SearchChar(int row, int col){     
    if(row < 0 || col < 0 || row > puzzle.length || col > puzzle[0].length)//Is row or col out of bounds?
        return 0;

    if(IsWord(puzzle[row][col]))
        return 1;       
    return 0;
}

從概念上講,我覺得我需要某種遞歸函數來處理這個問題,但我似乎無法理解它。 我什至不知道這是否是正確的方法。 我一直在StringBuilder附加StringBuilder但我仍然在為此苦苦掙扎。 我認為這個遞歸函數需要以puzzle[0][0] (或'C')為例,看看它是否在字典中。 接着是puzzle[0][0] + puzzle[0][1] (或'CA'),最后是puzzle[0][0] + puzzle[0][1] + puzzle[0][2] (或“貓”)。 然后必須垂直和對角線進行相同的操作。 我在嘗試通過位置更改返回到SearchChar函數以附加到原始字符時遇到問題,以便我可以查看它是否在DICTIONARY

對不起,如果這有點羅嗦,但我只是想給人一種我實際上正在嘗試解決這個問題的印象。 不僅僅是一些懶惰的程序員將一些問題復制粘貼到這里供其他人解決。 在此先感謝您的幫助!

我將向您展示如何逐步解決這個問題。

1. 從給定的拼圖中生成所有可能的單詞

要做到這一點,我們必須從拼圖的任何地方開始,向所有方向移動(除了前面的Point )以生成所有可能的單詞;

2. 為字典選擇合適的數據結構

我認為Trie是一個不錯的選擇,適合在這種情況下使用。

選擇Trie的最重要原因是,在搜索過程中,我們可以輕松測試字典中是否存在某個單詞,或者是否存在以通過拼圖搜索生成的單詞開頭的單詞。 因此,我們可以決定是否繼續搜索。

這將為我們節省大量時間並有助於正確生成單詞。 否則,我們將陷入無限循環……

3. 實施

Tire有幾種實現,但我編寫了自己的CharTrie

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * @author FaNaJ
 */
public final class CharTrie {

    /**
     * Pointer to root Node
     */
    private final Node root = new Node();

    /**
     * Puts the specified word in this CharTrie and increases its frequency.
     *
     * @param word word to put in this CharTrie
     * @return the previous frequency of the specified word
     */
    public int put(String word) {
        if (word.isEmpty()) {
            return 0;
        }
        Node current = root;
        for (int i = 0; i < word.length(); i++) {
            current = current.getChildren().computeIfAbsent(word.charAt(i), ch -> new Node());
        }
        return current.getAndIncreaseFrequency();
    }

    /**
     * @param word the word whose frequency is to be returned
     * @return the current frequency of the specified word or -1 if there isn't such a word in this CharTrie
     */
    public int frequency(String word) {
        if (word.isEmpty()) {
            return 0;
        }
        Node current = root;
        for (int i = 0; i < word.length() && current != null; i++) {
            current = current.getChildren().get(word.charAt(i));
        }
        return current == null ? -1 : current.frequency;
    }

    /**
     * @param word the word whose presence in this CharTrie is to be tested
     * @return true if this CharTrie contains the specified word
     */
    public boolean contains(String word) {
        return frequency(word) > 0;
    }

    /**
     * @return a CharTrie Iterator over the Nodes in this CharTrie, starting at the root Node.
     */
    public Iterator iterator() {
        return new Iterator(root);
    }

    /**
     * Node in the CharTrie.
     * frequency-children entry
     */
    private static final class Node {

        /**
         * the number of occurrences of the character that is associated to this Node,
         * at certain position in the CharTrie
         */
        private volatile int frequency = 0;
        private static final AtomicIntegerFieldUpdater<Node> frequencyUpdater
                = AtomicIntegerFieldUpdater.newUpdater(Node.class, "frequency");

        /**
         * the children of this Node
         */
        private Map<Character, Node> children;

        public Map<Character, Node> getChildren() {
            if (children == null) {
                children = new ConcurrentHashMap<>();
            }
            return children;
        }

        /**
         * Atomically increments by one the current value of the frequency.
         *
         * @return the previous frequency
         */
        private int getAndIncreaseFrequency() {
            return frequencyUpdater.getAndIncrement(this);
        }

    }

    /**
     * Iterator over the Nodes in the CharTrie
     */
    public static final class Iterator implements Cloneable {

        /**
         * Pointer to current Node
         */
        private Node current;

        private Iterator(Node current) {
            this.current = current;
        }

        /**
         * Returns true if the current Node contains the specified character in its children,
         * then moves to the child Node.
         * Otherwise, the current Node will not change.
         *
         * @param ch the character whose presence in the current Node's children is to be tested
         * @return true if the current Node's children contains the specified character
         */
        public boolean next(char ch) {
            Node next = current.getChildren().get(ch);
            if (next == null) {
                return false;
            }
            current = next;
            return true;
        }

        /**
         * @return the current frequency of the current Node
         */
        public int frequency() {
            return current.frequency;
        }

        /**
         * @return the newly created CharTrie Iterator, starting at the current Node of this Iterator
         */
        @Override
        @SuppressWarnings("CloneDoesntCallSuperClone")
        public Iterator clone() {
            return new Iterator(current);
        }

    }

}

WordGenerator

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.function.BiConsumer;

/**
 * @author FaNaJ
 */
public final class WordGenerator {

    private WordGenerator() {
    }

    public static void generate(char[][] table, CharTrie.Iterator iterator, BiConsumer<String, Integer> action) {
        final ForkJoinPool pool = ForkJoinPool.commonPool();
        final VisitorContext ctx = new VisitorContext(table, action);
        for (int y = 0; y < table.length; y++) {
            for (int x = 0; x < table[y].length; x++) {
                pool.invoke(new Visitor(new Point(x, y), null, "", iterator.clone(), ctx));
            }
        }
    }

    private static final class VisitorContext {

        private final char[][] table;
        private final BiConsumer<String, Integer> action;

        private VisitorContext(char[][] table, BiConsumer<String, Integer> action) {
            this.table = table;
            this.action = action;
        }

        private boolean validate(Point point) {
            Object c = null;
            try {
                c = table[point.getY()][point.getX()];
            } catch (ArrayIndexOutOfBoundsException ignored) {
            }
            return c != null;
        }

    }

    private static final class Visitor extends RecursiveAction {

        private final Point current;
        private final Point previous;
        private final CharTrie.Iterator iterator;
        private final VisitorContext ctx;

        private String word;

        private Visitor(Point current, Point previous, String word, CharTrie.Iterator iterator, VisitorContext ctx) {
            this.current = current;
            this.previous = previous;
            this.word = word;
            this.iterator = iterator;
            this.ctx = ctx;
        }

        @Override
        protected void compute() {
            char nextChar = ctx.table[current.getY()][current.getX()];
            if (iterator.next(nextChar)) {
                word += nextChar;
                int frequency = iterator.frequency();
                if (frequency > 0) {
                    ctx.action.accept(word, frequency);
                }
                List<Visitor> tasks = new ArrayList<>();
                for (Direction direction : Direction.values()) {
                    Point nextPoint = direction.move(current);
                    if (!nextPoint.equals(previous) && ctx.validate(nextPoint)) {
                        tasks.add(new Visitor(nextPoint, current, word, iterator.clone(), ctx));
                    }
                }
                invokeAll(tasks);
            }
        }

    }

}

請注意,我使用ForkJoinPoolRecursiveAction來加快搜索速度。

了解更多 :

其余課程:

PuzzleSolver

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.stream.Stream;

/**
 * @author FaNaJ
 */
public final class PuzzleSolver {

    private final CharTrie dictionary;

    public enum OrderBy {FREQUENCY_IN_DICTIONARY, FREQUENCY_IN_PUZZLE}

    public PuzzleSolver(CharTrie dictionary) {
        this.dictionary = dictionary;
    }

    public CharTrie getDictionary() {
        return dictionary;
    }

    public Stream<Word> solve(char[][] puzzle) {
        return solve(puzzle, OrderBy.FREQUENCY_IN_DICTIONARY);
    }

    public Stream<Word> solve(char[][] puzzle, OrderBy orderBy) {
        Stream<Word> stream = null;
        switch (orderBy) {
            case FREQUENCY_IN_DICTIONARY: {
                final Map<String, Integer> words = new ConcurrentHashMap<>();
                WordGenerator.generate(puzzle, dictionary.iterator(), words::put);
                stream = words.entrySet().stream()
                        .map(e -> new Word(e.getKey(), e.getValue()));
                break;
            }
            case FREQUENCY_IN_PUZZLE: {
                final Map<String, AtomicInteger> words = new ConcurrentHashMap<>();
                BiConsumer<String, Integer> action = (word, frequency) -> words.computeIfAbsent(word, s -> new AtomicInteger()).getAndIncrement();
                WordGenerator.generate(puzzle, dictionary.iterator(), action);
                stream = words.entrySet().stream()
                        .map(e -> new Word(e.getKey(), e.getValue().get()));
                break;
            }
        }
        return stream.sorted((a, b) -> b.compareTo(a));
    }

}

Point

import java.util.Objects;

/**
 * @author FaNaJ
 */
public final class Point {

    private final int x;
    private final int y;

    public Point() {
        this(0, 0);
    }

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    @Override
    public int hashCode() {
        return x * 31 + y;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        Point that = (Point) obj;
        return x == that.x && y == that.y;
    }

    @Override
    public String toString() {
        return "[" + x + ", " + y + ']';
    }

}

Word

/**
 * @author FaNaJ
 */
public final class Word implements Comparable<Word> {

    private final String value;
    private final int frequency;

    public Word(String value, int frequency) {
        this.value = value;
        this.frequency = frequency;
    }

    public String getValue() {
        return value;
    }

    public int getFrequency() {
        return frequency;
    }

    @Override
    public int hashCode() {
        return value.hashCode() * 31 + frequency;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Word that = (Word) o;
        return frequency == that.frequency && value.equals(that.value);
    }

    @Override
    public String toString() {
        return "{" +
                "value='" + value + '\'' +
                ", frequency=" + frequency +
                '}';
    }

    @Override
    public int compareTo(Word o) {
        return Integer.compare(frequency, o.frequency);
    }

}

Direction

/**
 * @author FaNaJ
 */
public enum Direction {

    UP(0, 1), UP_RIGHT(1, 1), UP_LEFT(-1, 1),
    RIGHT(1, 0), LEFT(-1, 0),
    DOWN(0, -1), DOWN_RIGHT(1, -1), DOWN_LEFT(-1, -1);

    private final int x, y;

    Direction(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public Point move(Point point) {
        return new Point(point.getX() + x, point.getY() + y);
    }

}

4. 測試一下

/**
 * @author FaNaJ
 */
public class Test {

    public static String[] DICTIONARY = {"OX", "CAT", "TOY", "AT", "DOG", "CATAPULT", "T", "AZOYZACZOTACXY"};

    public static void main(String[] args) {
        CharTrie trie = new CharTrie();
        for (String word : DICTIONARY) {
            trie.put(word);
        }

        PuzzleSolver solver = new PuzzleSolver(trie);

        char[][] puzzle = {
                {'C', 'A', 'T'},
                {'X', 'Z', 'T'},
                {'Y', 'O', 'T'}
        };
        solver.solve(puzzle, PuzzleSolver.OrderBy.FREQUENCY_IN_PUZZLE).forEach(System.out::println);
    }

}

輸出 :

{value='T', frequency=3}
{value='AT', frequency=2}
{value='CAT', frequency=2}
{value='TOY', frequency=2}
{value='OX', frequency=1}
{value='AZOYZACZOTACXY', frequency=1}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM