Java实现成语接龙

近来,想做一些算法练习,无意中想到了我们非常熟悉的成语接龙游戏(本次以四字成语为例),就想着如何用java实现成龙接龙的关键逻辑。说干就干,于是很快梳理了一下思路:

1、首先在数据库构建一个表,导入大量的成语,作为成语储备。(这是基础)

2、思考接龙逻辑,实现成语接龙。(这是核心)

首先,我们来构建成语库,根据成语接龙的游戏规则,思考了下,表至少需要以下字段:成语内容、成语首字、成语末尾字。如果成语接龙的规则再放宽,比如接龙的时候只考虑读音一致也行,那么再补充成语首字拼音、成语末尾字拼音两个字段。于是,表结构基本确定下来了,以Spring Data JPA为例,成语的Entity如下:

@Entity
@Table(name = "t_idiom")
public class IdiomEntity {
    @Id
    private String id = Util.getUUIDStr();
    private String value;//四字成语,如:龙飞凤舞
    private String wordS;//成语的首字
    private String wordE;//成语的末尾字
    private String pinyinS;//成语的首字拼音
    private String pinyinE;//成语的末尾字拼音
    private String paraphrase;//成语解释

    //为节约篇幅,此处省略get、set方法。
}

表建好之后就需要导入数据了,我从网上找了个txt版的成语大全,非常全,大概接近三万条成语。这么多数据,当然需要通过程序导入表里了,可以通过java读取txt文件的内容,加以逻辑,将数据存到表里。java读取txt文件的代码可自行研究学习,不在详细解释,因为这不是本文的重点。txt文件内容的格式如下:

读取txt文件并保存数据代码如下:

    @Override
    public void readTxt(String filePath) {
        File file = new File(filePath);
        StringBuilder result = new StringBuilder();
        try{
            BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
            String s = null;
            while((s = br.readLine())!=null){//使用readLine方法,一次读一行
                if (s.indexOf("拼音") > 0) {
                    String value = s.substring(0, s.indexOf("拼音")).trim();
                    if (value.length() == 4) {
                        IdiomEntity idiomEntityOld = idiomRepo.findFirstByValue(value);
                        if (idiomEntityOld == null) {
                        try {
                            String pinyin = s.substring(s.indexOf("拼音:") + 3, s.indexOf("释义"));
                            String paraphrase = s.substring(s.indexOf("释义:") + 3);
                            String wordS = value.substring(0,1);
                            String wordE = value.substring(3,4);
                            String pinyinS = pinyin.substring(0, pinyin.indexOf(" "));
                            String pinyinE = pinyin.substring(pinyin.lastIndexOf(" ") + 1);
                            IdiomEntity idiomEntity = new IdiomEntity();
                            idiomEntity.setValue(value);
                            idiomEntity.setWordS(wordS);
                            idiomEntity.setPinyinS(pinyinS);
                            idiomEntity.setWordE(wordE);
                            idiomEntity.setPinyinE(pinyinE);
                            idiomEntity.setParaphrase(paraphrase);
                            idiomRepo.save(idiomEntity);
                        } catch (Throwable e) {
                            continue;
                        }
                        }
                    }
                }
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
    }

 成语大全里已经给出了成语的汉语拼音了,非常方便,但是如果没有带拼音的话,我们也可以通过java代码获取汉字的汉语拼音,代码如下:

    /**
     * 汉字转为拼音
     * @param chinese
     * @return
     */
     public static String ToPinyin(String chinese){
         String pinyinStr = "";
         char[] newChar = chinese.toCharArray();
         HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
         defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
         defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
         for (int i = 0; i < newChar.length; i++) {
             if (newChar[i] > 128) {
                 try {
                     pinyinStr += PinyinHelper.toHanyuPinyinStringArray(newChar[i], defaultFormat)[0];
                 } catch (BadHanyuPinyinOutputFormatCombination e) {
                     e.printStackTrace();
                 }
             }else{
                 pinyinStr += newChar[i];
             }
         }
         return pinyinStr;
     }

读入成语数据,算是完成了第一步,如图:

一共28603条成语,成语库很丰富了。

接下来,最关键的一步,实现成语接龙的逻辑,就以最常见的游戏规则(接龙的时候前后两个成语的首尾字必须一致)为例。思路如下:根据输入的第一个成语(也就是龙头句),根据它的末尾字(也就是关节字)来接龙,我们可以去数据库里检索第二个成语(首字等于龙头句的末尾字)。检索到的第二个成语肯定不止一个,按照同样逻辑,检索下一个成语,每次检索都会有多个答案。就像一棵树一样,龙头句相当于树的根部,每次接龙多会分N个树杈,直到无法接上(到达叶子)。也就是说每条从树根到达某个叶子节点的路径都是成语接龙的答案。如何得到这些答案,并输出出来,核心就是利用递归和遍历。实现代码如下:

    @Override
    public void idiomPlay(String sIdiom) {   //sIdiom:输入的成语(龙头句)
        String wordE = sIdiom.substring(3, 4);   //关节字
        doPlay(sIdiom, wordE);
    }

    //递归方法
    public void doPlay(String idiomStr, String wordE){
        List<IdiomEntity> idiomEntities = idiomRepo.findAllByWordS(wordE);   //根据关节字检索下一条成语
        if (idiomEntities.size() > 0) {
            for (IdiomEntity entity : idiomEntities) {
                if (idiomStr.indexOf(entity.getValue()) > -1) {   //如果检索到的成语已经在之前的成语串里,则停止递归
                    System.out.println(idiomStr);
                } else {
                    doPlay(idiomStr + "->" + entity.getValue(), entity.getWordE());
                }
            }
        } else {   //检索不到下一条成语,该路径完成
            System.out.println(idiomStr);
        }
    }

输入“龙飞凤舞”,执行结果片段如下:

由于成语库非常大,接龙的答案会非常多,一时半会是执行不完的。在测试的过程中,发现有时会输出重复的答案,分析发现,在成语串已经非常长的情况下,检索下一条成语的时候如果检索到很多个,则可能会出现其中多个是已经在之前成语串中出现过,根据程序逻辑,这种情况会输出几条重复的答案,但是不会出现死循环。可以利用set集合去重,本文不再详述。

这样,一个简单的成语接龙就实现了,其他接龙玩法可依本例扩展,不再详述。

 

以下是一个基于Java连接MySQL数据库实现成语接龙的示例代码: ```java import java.sql.*; public class ChengYuJieLong { private Connection conn; private Statement stmt; private ResultSet rs; private String lastChengYu; public ChengYuJieLong() { try { Class.forName("com.mysql.cj.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/chengyu"; String username = "root"; String password = "password"; conn = DriverManager.getConnection(url, username, password); stmt = conn.createStatement(); lastChengYu = "起始成语"; } catch (Exception e) { e.printStackTrace(); } } public String getNextChengYu(String input) { String nextChengYu = ""; try { String sql = "select * from chengyu where pinyin like '" + input.charAt(input.length() - 1) + "%' and chengyu not like '%" + input + "%' order by rand() limit 1"; rs = stmt.executeQuery(sql); if (rs.next()) { nextChengYu = rs.getString("chengyu"); lastChengYu = nextChengYu; } else { nextChengYu = "没有符合要求的成语了"; } } catch (Exception e) { e.printStackTrace(); } return nextChengYu; } public String getLastChengYu() { return lastChengYu; } public void close() { try { rs.close(); stmt.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); } } } ``` 需要注意的是,需要提前在MySQL数据库中创建一个名为`chengyu`的数据库,并在其中创建一个名为`chengyu`的表,表结构如下: ```sql CREATE TABLE `chengyu` ( `id` int(11) NOT NULL AUTO_INCREMENT, `chengyu` varchar(255) NOT NULL, `pinyin` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` 表中需要包含成语的名称和拼音。在代码中,`getNextChengYu`方法根据用户输入的成语的最后一个字符,从数据库中随机选择一个以该字符开头的成语作为下一个回答。`getLastChengYu`方法返回上一个回答的成语,方便用户进行对话。`close`方法关闭连接和声明对象。
评论 113
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值