LeetCode ——word-break

本文探讨了一个字符串能否被分割成一个或多个字典中的单词的问题,并通过动态规划的方法给出了一个高效的解决方案。

题目描述

 

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

For example, given
s ="leetcode",
dict =["leet", "code"].

Return true because"leetcode"can be segmented as"leet code".

 

分析:

动态规划,设f(0,i)是指字符串s从0到i的子串能否分割。那么有状态转移方程:f(i)=f(j)&&f(j+1,i)

 

代码:

//代码是牛客网的代码 leetcode题目太多了不知道从何刷起 就打算先刷牛客了
class Solution {
public:
    bool wordBreak(string s, unordered_set<string> &dict) {
        bool *table = new bool[s.size()+1];
        for(int i=1;i<s.size()+1;++i)
            table[i]=0;
        table[0] = true;
        for(int i=1;i<=s.size();++i)
        {
            for(int j = 0;j<i;++j)
            {
                if(table[i])
                    break;
                table[i] = (table[j]&&dict.find(s.substr(j,i-j))!=dict.end());
                
            }
        }
        return table[s.size()];
    }
};

 

你的代码逻辑是: 1. 读取多行字符串,每行是一个单词; 2. 遇到 `#` 字符时停止输入,并截断该行中 `#` 及之后的内容; 3. 将所有单词按**长度从小到大排序**(你写的是 `a > b` 则交换,所以是升序); 4. 最后输出所有单词(包括空行或无效内容?)。 但你说“超时了”,说明这可能是某个在线判题系统(如 OJ)的题目,比如 **PAT、LeetCode、Codeforces** 等。我们来分析问题所在并优化。 --- ### ❗问题分析 #### 1. `fgets(word[n++], sizeof(word[n]), stdin);` 这里有严重错误! ```c word[n-1][strcspn(word[n-1], "\n")] = '\0'; ``` 你在调用 `fgets` 前就用了 `n++`,即: ```c fgets(word[n++], sizeof(word[n]), stdin); ``` 这意味着:第一次 `n=0`,你执行 `word[0]` 的读入,然后 `n` 变成 1; 但是 `sizeof(word[n])` 实际上是 `sizeof(word[1])` —— 虽然这个没错(每个都是 char[30]),**但更致命的是下标混乱和缓冲区未清。** 而且关键问题是:`sizeof(word[n])` 是 `30` 没错,但你应该用 `sizeof(word[0])` 或直接写 `30`,避免歧义。 更重要的是:你在 `fgets` 后立即处理换行符,这是对的,但顺序出错了。 #### ✅ 正确做法应为: ```c fgets(word[n], 30, stdin); word[n][strcspn(word[n], "\n")] = '\0'; // 去掉换行 n++; ``` 而不是先 `n++` 再访问 `word[n]`。 --- #### 2. `*pos = NULL;` 错误! ```c char *pos = strchr(word[n-1], '#'); *pos = NULL; // 错!NULL 是指针,不是字符! ``` 应该改为: ```c *pos = '\0'; // 正确:字符串结束符 ``` 否则可能引发未定义行为。 --- #### 3. 排序部分效率低 + 多余比较 你用了冒泡排序,时间复杂度 $O(n^2)$,当 n 较大时会超时。 建议改用 `qsort` 或至少修复逻辑。 另外你最后输出了 `n` 行,但实际上最后一行可能是空或者被 `#` 截断后的结果,甚至 `n` 包含了终止的那一行。 --- #### 4. 输入控制逻辑混乱 你是在读完一行后再判断有没有 `#`,然后设置 `is=1` 来退出循环。可以接受,但需注意截断后不要把 `#` 后面的内容算进去。 --- ### ✅ 改进后的完整代码(高效、正确) ```c #include <stdio.h> #include <string.h> #include <stdlib.h> // 比较函数:按字符串长度升序排序 int cmp(const void *a, const void *b) { char *s1 = (char *)a; char *s2 = (char *)b; return strlen(s1) - strlen(s2); } #define MAX_WORDS 1000 #define WORD_LEN 30 int main() { char words[MAX_WORDS][WORD_LEN]; int n = 0; char line[WORD_LEN]; while (n < MAX_WORDS && fgets(line, WORD_LEN, stdin) != NULL) { // 去除换行符 line[strcspn(line, "\n")] = '\0'; // 查找 '#' 并截断 char *hash_pos = strchr(line, '#'); if (hash_pos != NULL) { *hash_pos = '\0'; // 截断 if (strlen(line) > 0) { strcpy(words[n], line); n++; } break; // 遇到 # 结束输入 } // 正常保存当前行 if (strlen(line) > 0) { strcpy(words[n], line); n++; } } // 使用 qsort 快速排序,按长度升序 qsort(words, n, sizeof(words[0]), cmp); // 输出所有单词 for (int i = 0; i < n; i++) { printf("%s\n", words[i]); } return 0; } ``` --- ### ✅ 改进点总结: | 问题 | 修改 | |------|------| | `n++` 提前导致越界风险 | 先读再 `n++` | | `*pos = NULL` 错误 | 改为 `*pos = '\0'` | | 冒泡排序效率低 | 改用 `qsort` + 自定义比较函数 | | 循环条件不安全 | 加上 `n < MAX_WORDS` 防溢出 | | 多余输出 | 只在有效时才存入数组 | --- ### 📌 示例输入/输出 **输入:** ``` hello hi world# ``` **输出:** ``` hi hello world ``` > 注意:"world" 是从 "world#" 截断而来,仍参与排序。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值