【一起学数据结构与算法分析】第二篇:字谜游戏

上一章我们学习了求第K大元素的两种方法比较。

【一起学数据结构与算法分析】第一篇:取第K大数的两种方法比较

今天我们紧接着学习引论中的第二个问题:字谜游戏
在这里插入图片描述
首先我们分别定义字谜板(上图中的表格)和字典,字典暂且用一个简单的列表来模拟。

    companion object {
        /**
         * List as English word dictionary.
         */
        private val DICTIONARY = arrayOf("this", "two", "fat", "that")

        /**
         * 2d array named puzzle board.
         */
        private val PUZZLE_BOARD = arrayOf(
            arrayOf("t", "h", "i", "s"),
            arrayOf("w", "a", "t", "s"),
            arrayOf("o", "a", "h", "g"),
            arrayOf("f", "g", "d", "t")
        )
    }

有兴趣的同学可以补充一个完整的字典文件或者调用公共的api,不过这些都是细节,无碍我们写算法。
按照书中的提示:

检查每一个有序三元组(行、列、方向)验证是否有单词存在.

行、列倒好理解,可这个方向我们需要琢磨下,经过老朽慎密的思考,给出了如下定义:

    /**
     * All directions for finding out.
     * @param xStep is the offset when moving on x-axis.
     * @param yStep is the offset when moving on y-axis.
     */
    enum class Direction(val xStep: Int, val yStep: Int) {
        Left(-1, 0),
        Top(0, -1),
        Right(1, 0),
        Bottom(0, 1),
        TopLeft(-1, -1),
        TopRight(-1, 1),
        BottomLeft(1, -1),
        BottomRight(1, 1)
    }

就是说,首先共有8个方向,每个方向还带有两个步长,就拿第一个Left来说吧,表示左方向移动一次在【列】上移动-1个单位,在【行】上移动0个单位。
为了加深理解,老朽还是简单地补一张图吧。。。
在这里插入图片描述
方向定义好了,接下来就是循环了:先遍历字谜表的行,再遍历列,再根据方向遍历,再遍历取出来的字符串以判断是否在字典里……

听到这些遍历什么感受????
在这里插入图片描述
不说了,直接上代码。

 /**
     * Method1: For every direction of every letter inside of [PUZZLE_BOARD], detect whether the directed line contains
     * invalid word in [DICTIONARY].If word recognized, print its location with rows, cols, direction and letter count.
     */
    @Test
    fun detectWords() {
        // Loop puzzle board by rows.
        PUZZLE_BOARD.forEachIndexed { rows, rowsItems ->
            // Loop the puzzle board by cols.
            rowsItems.forEachIndexed { cols, _ ->
                // Loop by directions.
                Direction.values().forEachIndexed { _, direction ->
                    // Retrieve all string by rows, cols, and directions.
                    val suspiciousWords = getSuspiciousWords(PUZZLE_BOARD, rows, cols, direction)
                    // Loop the retrieved list
                    suspiciousWords?.forEachIndexed { _, s ->
                        // If the string is a valid word, print word and its positions.
                        if(isValidWord(s)) {
                            print(s, rows, cols, direction)
                        }
                    }
                }
            }
        }
    }

其中方法getSuspiciousWords就是根据起点和方向找到这个方向上的所有字符串。
在这里插入图片描述

/**
     *  For every direction of every letter inside of [PUZZLE_BOARD], retrieve all string on the the directed line with
     *  start point fixed.
     *  @param rows is rows of start point.
     *  @param cols is cols of start point.
     *  @param direction is the loop direction to find out words.
     *  @return list contains suspicious words, null or empty list if no string found out.
     */
    private fun getSuspiciousWords(array: Array<Array<String>>, rows: Int, cols: Int, direction: Direction): List<String>? {
        // return null if rows and cols exceed the range of array.
        if(array.isEmpty()) {
            return null
        }
        val arrayRows = array.size
        val arrayCols = array[0].size
        if(rows < 0 || rows >= arrayRows || cols < 0 || cols >= arrayCols) {
            return null
        }
        val resultList = ArrayList<String>()
        // To the assigned direction, add the positions string to the result list.
        var currentRows = rows
        var currentCols = cols
        // Add the first letter as well as the start point.
        val sb = StringBuilder(array[currentRows][currentCols])
        resultList.add(sb.toString())
        while (true) {
            currentRows += direction.yStep
            currentCols += direction.xStep
            // Check range!
            if(currentRows < 0 || currentRows >= arrayRows || currentCols < 0 || currentCols >= arrayCols) {
                break
            } else {
                resultList.add(sb.append(array[currentRows][currentCols]).toString())
            }
        }
        return resultList
    }

根据这个粗暴的算法,我们也顺利找到了字谜。
在这里插入图片描述

好了,主要代码就这么多,思路其实很简单,就是效率非常低下,不信你弄个64*64的字谜板并且用真实字典试试,估计够吃一把鸡了。

接下来我们分析下该算法的时间复杂度。
在这里插入图片描述

总时间复杂度为:O(n) * O(n) * 8 * (O(n) + O(n)) = O(n^3)。

其实就算使用如些粗暴的方法,我们仍可以适当优化,如按书中提示指定最大单词长度,如30,反正老朽的六级水平还没有看过30个字母以上的字词哈~

当然了,以上算法只是最直观的实现,还是那句话,慢慢把这本书看下去。其实学算法最有趣的事情,莫过于看到了更优解,而不是最优解。

最后再奉上代码地址:https://github.com/codersth/dsa-study.git
文件:WordRiddleTest.kt

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Meta章磊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值