520. 检测大写字母
我们定义,在以下情况时,单词的大写用法是正确的:
- 全部字母都是大写,比如
"USA"
。 - 单词中所有字母都不是大写,比如
"leetcode"
。 - 如果单词不只含有一个字母,只有首字母大写, 比如
"Google"
。
给你一个字符串 word
。如果大写用法正确,返回 true
;否则,返回 false
。
示例 1:
输入:word = "USA"
输出:true
示例 2:
输入:word = "FlaG"
输出:false
提示:
1 <= word.length <= 100
word
由小写和大写英文字母组成
cpp
#include <cstdio>
// 判断一个字符是否是大写字母
bool judgeBig(char c) {
// 判断字符c是否在'A'到'Z'之间
return c >= 'A' && c <= 'Z';
}
// 检查整个单词是否全部由大写字母组成
bool allBig(char* word) {
int i = 0;
int flag = 0; // 用于统计大写字母的数量
while (word[i] != '\0') { // 遍历整个单词
if (judgeBig(word[i])) { // 如果当前字符是大写字母
flag++; // 统计大写字母数量
}
i++; // 移动到下一个字符
}
// 如果大写字母的数量等于单词的长度,说明全部是大写字母
return flag == i;
}
// 检查整个单词是否全部由小写字母组成
bool allSmall(char* word) {
int i = 0;
int flag = 0; // 用于统计小写字母的数量
while (word[i] != '\0') { // 遍历整个单词
if (!judgeBig(word[i])) { // 如果当前字符不是大写字母,即为小写字母
flag++; // 统计小写字母数量
}
i++; // 移动到下一个字符
}
// 如果小写字母的数量等于单词的长度,说明全部是小写字母
return flag == i;
}
// 检查单词是否首字母大写,其余字母小写
bool hasSmallAndBig(char* word) {
int i = 0;
int flag = 0; // 用于统计小写字母的数量
if (judgeBig(word[0])) { // 检查首字母是否是大写字母
i++; // 移动到第二个字符
flag++; // 首字母是大写,统计小写字母数量从1开始
while (word[i] != '\0') { // 遍历剩余字符
if (!judgeBig(word[i])) { // 如果当前字符是小写字母
flag++; // 统计小写字母数量
}
i++; // 移动到下一个字符
}
} else {
// 首字母不是大写字母,直接返回false
return false;
}
// 如果小写字母的数量加上首字母等于单词的长度,说明符合条件
return i == flag;
}
// 综合判断单词的大小写使用是否符合规则
bool detectCapitalUse(char* word) {
// 如果全部是大写字母,返回true
if (allBig(word)) {
return true;
}
// 如果全部是小写字母,返回true
if (allSmall(word)) {
return true;
}
// 如果首字母大写,其余小写,返回true
return hasSmallAndBig(word);
}
int main() {
char* word = {"Google"}; // 测试单词
if (detectCapitalUse(word)) { // 调用函数判断
puts("true"); // 如果符合条件,输出true
} else {
puts("false"); // 如果不符合条件,输出false
}
}
大佬写的
cpp
#include <cstdio> // 包含标准输入输出库,用于printf、puts等函数
#include <cctype> // 包含字符处理函数,如isupper
#include <cstring> // 包含字符串处理函数,如strlen
bool detectCapitalUse(char* word) {
int flag = 0; // 用于统计大写字母的数量
int i = 0; // 循环计数器
while (word[i] != '\0') { // 遍历单词中的每个字符,直到字符串结束符
if (isupper(word[i])) { // 检查当前字符是否为大写字母
flag++; // 如果是大写字母,增加计数器
}
i++; // 移动到下一个字符
}
// 判断是否符合三种情况之一:
// 1. 全小写:flag == 0
// 2. 全大写:flag == strlen(word)
// 3. 首字母大写:flag == 1 且首字母是大写
return flag == 0 || flag == strlen(word) || (flag == 1 && isupper(word[0]));
}
int main() {
char* word = {"Google"}; // 定义并初始化测试单词
if (detectCapitalUse(word)) { // 调用函数判断大小写规则
puts("true"); // 如果符合条件,输出"true"
} else {
puts("false"); // 如果不符合条件,输出"false"
}
}
125. 验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s
,如果它是 回文串 ,返回 true
;否则,返回false
。
示例 1:
输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。
示例 2:
输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。
示例 3:
输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。
cpp
#include <cstdio> // 提供输入输出函数,如printf和scanf
#include <cstdlib> // 提供内存管理函数,如malloc和free
#include <cstring> // 提供字符串操作函数,如strlen和strcpy
#include <cctype> // 提供字符分类函数,如isdigit和isalpha
/**
* 判断一个字符串是否是回文
*/
bool isPalindrome(char* s) {
if (s == NULL) {
return false; // 处理空指针情况
}
int len = strlen(s); // 获取字符串的长度
if (len == 0) {
return true; // 空字符串视为回文
}
// 分配内存以存储处理后的字符串
char* temp = (char*)malloc(len * sizeof(char));
if (temp == NULL) {
return false; // 内存分配失败
}
int j = 0; // 用于跟踪处理后字符串的长度
for (int i = 0; i < len; ++i) {
char c = s[i];
if (isdigit(c) || isalpha(c)) { // 检查是否是数字或字母
if (isalpha(c)) {
temp[j++] = tolower(c); // 将字母转换为小写并存入temp
} else {
temp[j++] = c; // 将数字直接存入temp
}
}
}
// 如果处理后的字符串为空,视为回文
if (j == 0) {
free(temp); // 释放内存
return true;
}
//temp[j] = '\0'; // 添加字符串结束符
// 检查回文
int left = 0;
int right = j - 1;
while (left < right) {
if (temp[left] != temp[right]) { // 如果左右字符不匹配
free(temp); // 释放内存
return false; // 不是回文
}
left++; // 左指针右移
right--; // 右指针左移
}
free(temp); // 释放内存
return true; // 是回文
}
int main() {
char* s = "a"; // 定义一个字符串
if (isPalindrome(s)) { // 判断是否是回文
printf("true\n"); // 如果是回文,输出true
} else {
printf("false\n"); // 如果不是回文,输出false
}
return 0; // 程序结束
}
14. 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i]
如果非空,则仅由小写英文字母组成
cpp
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
// 函数:计算字符串数组的最长公共前缀
char* longestCommonPrefix(char** strs, int strsSize) {
if (strsSize == 0) return ""; // 如果字符串数组为空,返回空字符串
// 获取第一个字符串的长度,作为初始最小长度
int minLength = strlen(strs[0]);
for (int i = 0; i < strsSize; ++i) {
int currentLength = strlen(strs[i]); // 获取当前字符串的长度
if (currentLength < minLength) {
minLength = currentLength; // 更新最小长度
}
}
// 创建一个字符数组,用于存储最长公共前缀
// 需要分配 minLength + 1 的空间,以便存储字符串的终止符 '\0'
char* ans = (char*)malloc((minLength + 1) * sizeof(char));
memset(ans, '\0', minLength + 1); // 初始化数组为全零
// 遍历每个字符位置,直到最小长度
for (int i = 0; i < minLength; ++i) {
char currentChar = strs[0][i]; // 当前比较的字符(从第一个字符串中取)
for (int j = 0; j < strsSize; ++j) {
if (currentChar != strs[j][i]) { // 如果当前字符在某个字符串中不匹配
return ans; // 返回当前已匹配的公共前缀
}
}
ans[i] = currentChar; // 如果所有字符串在该位置字符相同,则将其加入结果
}
// 如果所有字符串完全相同,返回完整的公共前缀
return ans;
}
int main() {
// 定义字符串数组
const char* strings[] = {"aaaa", "aaaa", "aaaa"};
int numStrings = sizeof(strings) / sizeof(strings[0]); // 计算字符串数组的大小
int maxLen = 200; // 每个字符串的最大长度(假设值)
// 分配内存,用于存储字符串数组
char** strs = (char**)malloc(numStrings * sizeof(char*));
for (int i = 0; i < numStrings; i++) {
strs[i] = (char*)malloc(maxLen * sizeof(char)); // 为每个字符串分配内存
strcpy(strs[i], strings[i]); // 复制字符串内容
}
// 调用函数计算最长公共前缀
char* ans = longestCommonPrefix(strs, numStrings);
// 打印结果
printf("%s\n", ans);
// 释放动态分配的内存
for (int i = 0; i < numStrings; i++) {
free(strs[i]); // 释放每个字符串的内存
}
free(strs); // 释放字符串数组的内存
free(ans); // 释放最长公共前缀的内存
return 0;
}
344. 反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s
的形式给出。
不要给另外的数组分配额外的空间,你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例 2:
输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
cpp
#include <cstdio>
#include <cstring>
void reverseString(char* s, int sSize) {
size_t half = sSize / 2;
for (int i = 0; i < half; i++) {
char temp = s[i];
s[i] = s[sSize - i - 1];
s[sSize - i - 1] = temp;
}
}
int main() {
char s[] = {'h','e','l','l','o'};
int length = sizeof(s) / sizeof(s[0]);
reverseString(s,length);
for (int i = 0; i < length; ++i) {
printf("%c ",s[i]);
}
}
541. 反转字符串 II
给定一个字符串 s
和一个整数 k
,从字符串开头算起,每计数至 2k
个字符,就反转这 2k
字符中的前 k
个字符。
- 如果剩余字符少于
k
个,则将剩余字符全部反转。 - 如果剩余字符小于
2k
但大于或等于k
个,则反转前k
个字符,其余字符保持原样。
示例 1:
输入:s = "abcdefg", k = 2
输出:"bacdfeg"
示例 2:
输入:s = "abcd", k = 2
输出:"bacd"
cpp
#include <cstdio>
#include <cstring>
#include <cstdlib>
// 反转字符串中从start位置开始的sSize个字符
void reverseString(char* s, int start, int sSize) {
int half = sSize / 2; // 计算需要交换的次数
for (int i = 0; i < half; i++) {
char temp = s[start + i]; // 临时存储当前字符
s[start + i] = s[start + sSize - i - 1]; // 将当前字符替换为对应位置的字符
s[start + sSize - i - 1] = temp; // 将对应位置的字符替换为临时存储的字符
}
}
// 根据给定的整数k,反转字符串中的字符
char* reverseStr(char* s, int k) {
int length = strlen(s); // 计算字符串的长度
int n = length / (2 * k); // 计算可以分成多少个完整的2k块
int last = length % (2 * k); // 计算剩余的字符数
// 处理每个完整的2k块
for (int i = 0; i < n; ++i) {
reverseString(s, i * 2 * k, k); // 反转当前块的前k个字符
}
// 处理剩余的字符
if (last > 0) {
if (last >= k) {
reverseString(s, n * 2 * k, k); // 如果剩余字符数大于等于k,反转前k个字符
} else {
reverseString(s, n * 2 * k, last); // 如果剩余字符数小于k,反转全部剩余字符
}
}
return s; // 返回处理后的字符串
}
int main() {
char s[] = {'a','b','c','d','e','f','g'}; // 定义一个字符数组
char * ans = reverseStr(s, 2); // 调用reverseStr函数,k=2
for (int i = 0; i < strlen(ans); ++i) {
printf("%c ", ans[i]); // 打印处理后的字符串
}
return 0;
}
557. 反转字符串中的单词 III
给定一个字符串 s
,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例 1:
输入:s = "Let's take LeetCode contest"
输出:"s'teL ekat edoCteeL tsetnoc"
示例 2:
输入: s = "Mr Ding"
输出:"rM gniD"
提示:
1 <= s.length <= 5 * 10的4次方
s
包含可打印的 ASCII 字符。s
不包含任何开头或结尾空格。s
里 至少 有一个词。s
中的所有单词都用一个空格隔开
cpp
#include <cstdio>
#include <cstring>
#include <cstdlib>
// 函数:反转字符串中从start位置开始的sSize个字符
void reverseString(char* s, int start, int sSize) {
int half = sSize / 2; // 计算需要交换的次数(一半的长度)
for (int i = 0; i < half; i++) {
char temp = s[start + i]; // 临时存储当前字符
s[start + i] = s[start + sSize - i - 1]; // 将当前字符替换为对应位置的字符
s[start + sSize - i - 1] = temp; // 将对应位置的字符替换为临时存储的字符
}
}
// 函数:反转字符串中的每个单词
char* reverseWords(char* s) {
int len = strlen(s); // 获取字符串长度
printf("Original length: %d\n", len);
int start = 0; // 当前单词的起始位置
int length = 0; // 当前单词的长度
for (int i = 0; i < len; ++i) {
if (s[i] != ' ') { // 如果当前字符不是空格
length++; // 增加当前单词的长度
} else { // 如果遇到空格,则当前单词结束
reverseString(s, start, length); // 反转当前单词
start = i + 1; // 更新下一个单词的起始位置
length = 0; // 重置当前单词的长度
}
}
// 处理最后一个单词(可能后面没有空格)
reverseString(s, start, length);
// puts(s); // 打印反转后的字符串(调试用)
return s; // 返回反转后的字符串
}
int main() {
char s[] = "Let's take LeetCode contest"; // 定义一个字符数组
char* ans = reverseWords(s); // 调用reverseWords函数反转每个单词
for (int i = 0; i < strlen(ans); ++i) {
printf("%c", ans[i]); // 打印处理后的字符串
}
return 0;
}
58. 最后一个单词的长度
给你一个字符串 s
,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串
示例 1:
输入:s = "Hello World"
输出:5
解释:最后一个单词是“World”,长度为 5。
示例 2:
输入:s = " fly me to the moon "
输出:4
解释:最后一个单词是“moon”,长度为 4。
示例 3:
输入:s = "luffy is still joyboy"
输出:6
解释:最后一个单词是长度为 6 的“joyboy”。
提示:
1 <= s.length <= 10的4次方
s
仅有英文字母和空格' '
组成s
中至少存在一个单词
cpp
#include <cstdio>
#include <cstring>
// 函数:反转字符串
void reverseString(char* str) {
size_t length = strlen(str); // 获取字符串长度
size_t half = length / 2; // 需要反转的前半部分长度
// 交换前半部分和后半部分的字符
for (int i = 0; i < half; i++) {
char temp = str[i]; // 保存前半部分的字符
str[i] = str[length - i - 1]; // 将后半部分的字符放到前半部分的位置
str[length - i - 1] = temp; // 将前半部分的字符放到后半部分的位置
}
}
// 函数:计算字符串中最后一个单词的长度
int lengthOfLastWord(char* s) {
reverseString(s); // 先反转整个字符串,以便从开头开始找第一个单词
size_t length = strlen(s); // 获取反转后的字符串长度
int i = 0; // 用于遍历字符串的指针
int index = 0; // 记录第一个非空格字符的索引
// 找到第一个非空格字符的位置
while (i < length) {
if (s[i] != ' ') { // 如果当前字符不是空格
index = i; // 记录当前位置为第一个单词的起始位置
break; // 跳出循环,开始统计单词长度
}
i++; // 跳过空格
}
// 统计从起始位置到下一个空格的距离(即最后一个单词的长度)
int size = 0;
for (int j = index; j < length; ++j) {
if (s[j] != ' ') { // 如果当前字符不是空格
size++; // 增加单词长度
} else { // 如果遇到空格
break; // 结束统计
}
}
return size; // 返回最后一个单词的长度
}
int main() {
// 定义测试字符串
char s[] = " fly me to the moon ";
// 计算最后一个单词的长度
int lastWordLength = lengthOfLastWord(s);
// 输出结果
printf("The length of the last word is: %d\n", lastWordLength);
return 0;
}
389. 找不同
给定两个字符串 s
和 t
,它们只包含小写字母。
字符串 t
由字符串 s
随机重排,然后在随机位置添加一个字母。
请找出在 t
中被添加的字母。
示例 1:
输入:s = "abcd", t = "abcde"
输出:"e"
解释:'e' 是那个被添加的字母。
示例 2:
输入:s = "", t = "y"
输出:"y"
提示:
0 <= s.length <= 1000
t.length == s.length + 1
s
和t
只包含小写字母
cpp
#include <cstdio>
#include <cstring>
#include <cstdlib>
// 函数:找出字符串 t 中相对于字符串 s 多出的一个字符
char findTheDifference(char* s, char* t) {
// 如果 s 为空,则 t 的第一个字符就是答案
if (s == NULL) {
return t[0];
}
// 计算字符串 t 和 s 的长度
int t_len = strlen(t);
int s_len = strlen(s);
// 创建两个数组,用于统计 26 个字母在 t 和 s 中的出现次数
int* ans1 = (int*) malloc(26 * sizeof(int)); // 用于 t
int* ans2 = (int*) malloc(26 * sizeof(int)); // 用于 s
// 初始化数组为 0
memset(ans1, 0, 26 * sizeof(int));
memset(ans2, 0, 26 * sizeof(int));
// 统计 t 中每个字母的出现次数
for (int i = 0; i < t_len; ++i) {
int index = t[i] - 'a'; // 计算字母的索引(a=0, b=1, ..., z=25)
ans1[index]++;
}
// 统计 s 中每个字母的出现次数
for (int i = 0; i < s_len; ++i) {
int index = s[i] - 'a'; // 计算字母的索引
ans2[index]++;
}
// 比较两个数组,找出出现次数不同的字母
char temp = '\0'; // 用于存储结果
for (int i = 0; i < 26; ++i) {
if (ans1[i] != ans2[i]) { // 如果某个字母的出现次数不同
temp = i + 'a'; // 计算对应的字符
break; // 找到后立即退出循环
}
}
// 释放动态分配的内存
free(ans1);
free(ans2);
free(s);
free(t);
return temp; // 返回多出的字符
}
int main() {
char s[] = "abcd"; // 定义一个字符数组 s
char t[] = "abcde"; // 定义一个字符数组 t
char ans = findTheDifference(s, t); // 调用 findTheDifference 函数
printf("%c ", ans); // 打印结果
return 0;
}
383. 赎金信
给你两个字符串:ransomNote
和 magazine
,判断 ransomNote
能不能由 magazine
里面的字符构成。
如果可以,返回 true
;否则返回 false
。
magazine
中的每个字符只能在 ransomNote
中使用一次。
示例 1:
输入:ransomNote = "a", magazine = "b"
输出:false
示例 2:
输入:ransomNote = "aa", magazine = "ab"
输出:false
示例 3:
输入:ransomNote = "aa", magazine = "aab"
输出:true
提示:
1 <= ransomNote.length, magazine.length <= 105
ransomNote
和magazine
由小写英文字母组成
cpp
#include <cstdio>
#include <cstring>
#include <cstdlib>
// 函数:判断赎金信字符串能否用杂志中的字符构造
bool canConstruct(char* ransomNote, char* magazine) {
// 获取杂志和赎金信的长度
int magazine_len = strlen(magazine);
int ransomNote_len = strlen(ransomNote);
// 创建两个数组,用于统计每个字母在杂志和赎金信中的出现次数
int* ans1 = (int*) malloc(26 * sizeof(int)); // 统计杂志中的字母次数
memset(ans1, 0, 26 * sizeof(int));
int* ans2 = (int*) malloc(26 * sizeof(int)); // 统计赎金信中的字母次数
memset(ans2, 0, 26 * sizeof(int));
// 统计杂志中每个字母的出现次数
for (int i = 0; i < magazine_len; ++i) {
int index = magazine[i] - 'a'; // 将字符转换为对应的索引(0-25)
ans1[index]++; // 增加对应字符的计数
}
// 打印杂志的字母计数(调试用)
printf("magazine\t:");
for (int i = 0; i < 26; ++i) {
printf("%d ", ans1[i]);
}
printf("\n");
// 统计赎金信中每个字母的出现次数
for (int i = 0; i < ransomNote_len; ++i) {
int index = ransomNote[i] - 'a'; // 将字符转换为对应的索引
ans2[index]++; // 增加对应字符的计数
}
// 打印赎金信的字母计数(调试用)
printf("ransomNote\t:");
for (int i = 0; i < 26; ++i) {
printf("%d ", ans2[i]);
}
printf("\n");
// 找出杂志和赎金信中的最大字母索引值
int max1 = magazine[0] - 'a';
int max2 = ransomNote[0] - 'a';
// 更新杂志的最大字母索引值
for (int i = 0; i < magazine_len; ++i) {
int temp = magazine[i] - 'a';
if (temp > max1) {
max1 = temp;
}
}
// 更新赎金信的最大字母索引值
for (int i = 0; i < ransomNote_len; ++i) {
int temp = ransomNote[i] - 'a';
if (temp > max2) {
max2 = temp;
}
}
// 打印最大字母索引值
printf("max1:%d max2:%d\n", max1, max2);
// 取最大的字母索引值
int max = max1 > max2 ? max1 : max2;
int flag = 0; // 用于记录满足条件的字母数量
// 遍历所有可能用到的字母索引
for (int i = 0; i <= max; ++i) {
if (ans1[i] >= ans2[i]) { // 如果杂志中的字母计数 >= 赎金信中的
flag++; // 增加满足条件的字母数量
}
}
// 打印调试信息
printf("\nmax:%d ,", max);
printf("\nflag:%d", flag);
printf("\nransomNote_len:%d", ransomNote_len);
// 如果所有需要的字母数量都在杂志中足够,则返回 true
if (flag == max + 1) {
return true;
}
// 释放动态分配的内存
free(ans1);
free(ans2);
return false;
}
int main() {
// 定义测试字符串
char ransomNote[] = "fihjjjjei";
char magazine[] = "hjibagacbhadfaefdjaeaebgi";
// 调用 canConstruct 函数并打印结果
bool ans = canConstruct(ransomNote, magazine);
if (ans) {
printf("true");
} else {
printf("false");
}
return 0;
}
大佬写的
cpp
#include <stdio.h>
#include <string.h>
bool canConstruct(char* ransomNote, char* magazine) {
int i = 0; // 初始化遍历指针,用于遍历 ransomNote 字符串
// 遍历 ransomNote 的每个字符,直到遇到字符串结束符 '\0'
while (ransomNote[i] != '\0') {
// 使用 strchr 函数在 magazine 中查找当前字符
char* found = strchr(magazine, ransomNote[i]);
// 如果找不到当前字符,返回 false
if (found == NULL) {
return false;
} else {
// 计算找到的字符在 magazine 中的索引位置
int index = found - magazine;
// 将找到的字符标记为 '#',避免重复使用
magazine[index] = '#';
// 继续检查 ransomNote 的下一个字符
i++;
}
}
// 如果所有字符都能找到并且标记,则返回 true
return true;
}
int main() {
char ransomNote[] = "aa";
char magazine[] = "aab";
// 调用 canConstruct 函数
if (canConstruct(ransomNote, magazine)) {
printf("true\n");
} else {
printf("false\n");
}
return 0;
}
242. 有效的字母异位词
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的
字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
提示:
1 <= s.length, t.length <= 5 * 104
s
和t
仅包含小写字母
cpp
#include <cstdio> // 用于标准输入输出
#include <cstring> // 用于字符串处理函数,如strlen和strchr
// 函数:判断两个字符串是否是字母异位词
// 参数:s 和 t 是两个字符指针,指向要比较的字符串
// 返回值:如果s和t是字母异位词,返回true;否则返回false
bool isAnagram(char* s, char* t) {
// 首先检查两个字符串的长度是否相等
// 如果长度不同,直接返回false
if (strlen(s) != strlen(t)) {
return false;
}
// 初始化索引i,用于遍历字符串t
int i = 0;
while (t[i] != '\0') {
// 在字符串s中查找字符t[i]
char* found = strchr(s, t[i]);
// 如果没有找到字符t[i],说明s和t不是字母异位词
if (found == NULL) {
return false;
} else {
// 计算找到的字符在s中的索引
int index = found - s;
// 将该字符标记为已使用,避免重复计数
s[index] = '#';
// 继续处理下一个字符
i++;
}
}
// 所有字符都成功匹配,返回true
return true;
}
int main() {
// 定义两个字符串s和t
char s[] = "ab";
char t[] = "a";
// 调用isAnagram函数检查是否是字母异位词
if (isAnagram(s, t)) {
printf("true\n");
} else {
printf("false\n");
}
return 0;
}
2094. 找出 3 位偶数
给你一个整数数组 digits
,其中每个元素是一个数字(0 - 9
)。数组中可能存在重复元素。
你需要找出 所有 满足下述条件且 互不相同 的整数:
- 该整数由
digits
中的三个元素按 任意 顺序 依次连接 组成。 - 该整数不含 前导零
- 该整数是一个 偶数
例如,给定的 digits
是 [1, 2, 3]
,整数 132
和 312
满足上面列出的全部条件。
将找出的所有互不相同的整数按 递增顺序 排列,并以数组形式返回*。*
示例 1:
输入:digits = [2,1,3,0]
输出:[102,120,130,132,210,230,302,310,312,320]
解释:
所有满足题目条件的整数都在输出数组中列出。
注意,答案数组中不含有 奇数 或带 前导零 的整数。
示例 2:
输入:digits = [2,2,8,8,2]
输出:[222,228,282,288,822,828,882]
解释:
同样的数字(0 - 9)在构造整数时可以重复多次,重复次数最多与其在 digits中出现的次数一样。
在这个例子中,数字 8 在构造 288、828 和 882 时都重复了两次。
示例 3:
输入:digits = [3,7,5]
输出:[]
解释:使用给定的 digits 无法构造偶数。
提示:
3 <= digits.length <= 100
0 <= digits[i] <= 9
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 统计数组元素的频率
int* getPL(int* digits, int digitsSize) {
// 使用 calloc 初始化内存为零
int* pl = (int*)calloc(10, sizeof(int));
if (pl == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
for (int i = 0; i < digitsSize; ++i) {
int num = digits[i];
if (num < 0 || num > 9) {
printf("Invalid digit: %d\n", num);
continue;
}
pl[num]++;
}
return pl;
}
// 检查三位数 num 是否可能
bool isPossible(int num, int* pl) {
int digits[3];
digits[0] = num / 100;
digits[1] = (num / 10) % 10;
digits[2] = num % 10;
int tempPL[10];
memcpy(tempPL, pl, sizeof(tempPL));
for (int i = 0; i < 3; ++i) {
if (tempPL[digits[i]] == 0) {
return false;
}
tempPL[digits[i]]--;
}
return true;
}
// 添加数字到结果数组
void addNumber(int num, int* pl, int* result, int* count) {
if (isPossible(num, pl)) {
result[(*count)++] = num;
}
}
// 比较函数用于 qsort
int compare(const void* a, const void* b) {
return (*(int*)a - *(int*)b);
}
// 找出全部的偶数,按照递增的顺序排列
int* findEvenNumbers(int* digits, int digitsSize, int* returnSize) {
// 获取频率
int* pl = getPL(digits, digitsSize);
for (int i = 0; i < 10; ++i) {
printf("%d ", pl[i]);
}
printf("\n");
// 生成全部的三位数
// 结果数组,最大1000个数
int* result = (int*)calloc(1000, sizeof(int));
if (result == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
int count = 0; // count 是添加元素的位置
for (int i = 100; i < 1000; i += 2) { // 生成三位偶数
addNumber(i, pl, result, &count);
}
qsort(result, count, sizeof(int), compare);
*returnSize = count;
return result;
}
int main() {
int digits[] = {2, 1, 3, 0};
int digitsSize = sizeof(digits) / sizeof(digits[0]);
int returnSize = 0;
int* ans = findEvenNumbers(digits, digitsSize, &returnSize);
for (int i = 0; i < returnSize; ++i) {
printf("%d ", ans[i]);
}
printf("\n");
free(ans); // 释放分配的内存
return 0;
}
大佬写的,还得是大佬
cpp
#include <stdio.h>
#include <stdlib.h>
// 函数:查找并返回由给定数字组成的三位偶数(按递增顺序排列)
// 参数:
// - digits: 输入数组,包含可用的数字
// - digitsSize: 输入数组的大小
// - returnSize: 输出结果数组的大小
int* findEvenNumbers(int* digits, int digitsSize, int* returnSize) {
// 统计每个数字(0-9)的出现频率
int num[10] = {0}; // 用于记录0-9数字的出现次数
for (int i = 0; i < digitsSize; i++) {
num[digits[i]]++; // 统计每个数字出现的次数
}
// 分配内存以存储结果数组,最多可能有1000个三位数
int *ans = (int *)malloc(sizeof(int) * 1000); // 动态分配内存,大小为1000个整数
int idx = 0; // 用于记录当前结果数组中添加的元素数目
// 遍历所有可能的三位偶数,三位数的范围是100-999,步长为2(确保个位是偶数)
for (int i = 100; i <= 998; i += 2) { // 为什么是998?因为步长为2,最后一位是998
// 创建一个数组,用于临时统计当前三位数各个数字的出现次数
int cnt[10] = {0};
int tmp = i; // 复制当前三位数,用于分解
// 分解三位数,检查是否可以由输入数组中的数字组成
while (tmp > 0) {
int c = tmp % 10; // 获取当前数字的最后一位
cnt[c]++; // 增加该数字的出现次数
// 如果当前数字的出现次数超过输入数组中该数字的可用次数,说明无法组成
if (cnt[c] > num[c]) {
break; // 跳出循环,停止分解
}
tmp /= 10; // 去除最后一位
}
// 如果分解后的tmp等于0,说明分解成功,所有数字的出现次数都未超过输入数组的可用次数
if (tmp == 0) {
ans[idx++] = i; // 将该三位数添加到结果数组中
}
}
// 返回结果数组的大小
*returnSize = idx;
return ans; // 返回结果数组
}
// 示例测试函数
int main() {
int digits[] = {2, 1, 3, 0}; // 输入数字数组
int digitsSize = sizeof(digits) / sizeof(digits[0]); // 数组大小
int returnSize;
int *ans = findEvenNumbers(digits, digitsSize, &returnSize); // 调用函数
// 打印结果
for (int i = 0; i < returnSize; i++) {
printf("%d ", ans[i]);
}
printf("\n");
// 释放分配的内存(可选,取决于使用场景)
free(ans);
return 0;
}
824. 山羊拉丁文
给你一个由若干单词组成的句子 sentence
,单词间由空格分隔。每个单词仅由大写和小写英文字母组成。
请你将句子转换为 " 山羊拉丁文(Goat Latin )"(一种类似于 猪拉丁文 - Pig Latin 的虚构语言)。山羊拉丁文的规则如下:
- 如果单词以元音开头(
'a'
,'e'
,'i'
,'o'
,'u'
),在单词后添加"ma"
。- 例如,单词
"apple"
变为"applema"
。
- 例如,单词
- 如果单词以辅音字母开头(即,非元音字母),移除第一个字符并将它放到末尾,之后再添加
"ma"
。- 例如,单词
"goat"
变为"oatgma"
。
- 例如,单词
- 根据单词在句子中的索引,在单词最后添加与索引相同数量的字母
'a'
,索引从1
开始。- 例如,在第一个单词后添加
"a"
,在第二个单词后添加"aa"
,以此类推。
- 例如,在第一个单词后添加
返回将 sentence
转换为山羊拉丁文后的句子。
示例 1:
输入:sentence = "I speak Goat Latin"
输出:"Imaa peaksmaaa oatGmaaaa atinLmaaaaa"
示例 2:
输入:sentence = "The quick brown fox jumped over the lazy dog"
输出:"heTmaa uickqmaaa rownbmaaaa oxfmaaaaa umpedjmaaaaaa overmaaaaaaa hetmaaaaaaaa azylmaaaaaaaaa ogdmaaaaaaaaaa"
提示:
1 <= sentence.length <= 150
sentence
由英文字母和空格组成sentence
不含前导或尾随空格sentence
中的所有单词由单个空格分隔
cpp
#include <cstdio> // 包含输入输出流头文件
#include <cstdlib> // 包含标准库函数头文件
#include <cstring> // 包含字符串处理函数头文件
#include <cctype> // 包含字符处理函数头文件
/**
* isVowel函数判断一个字符是否是元音字母。
*
* @param c 要判断的字符。
* @return 返回true如果字符是元音字母,否则返回false。
*/
bool isVowel(char c) {
char lower = tolower(c); // 将字符转换为小写
return lower == 'a' || lower == 'e' || lower == 'i' || lower == 'o' || lower == 'u'; // 检查是否是元音字母
}
/**
* process函数将单个单词转换为Goat Latin。
*
* @param word 输入的单词。
* @param result 用于存储转换后结果的字符串。
* @param pos 指向结果字符串的当前位置。
* @param no 当前单词的序号。
*/
void process(char* word, char* result, int* pos, int no) {
int len = strlen(word); // 获取单词长度
int j = *pos; // 获取当前结果字符串的写入位置
// 检查单词是否以元音字母开头
if (isVowel(word[0])) {
strcpy(&result[j], word); // 如果是元音开头,直接拷贝单词到结果
j += len; // 更新结果位置
} else {
// 如果单词以辅音开头,将首字母移到末尾
for (int i = 1; i < len; i++) { // 从第二个字符开始拷贝
result[j++] = word[i];
}
result[j++] = word[0]; // 将首字母添加到末尾
}
// 添加"ma"和相应数量的"a"
strcpy(&result[j], "ma"); // 添加"ma"
j += 2; // 更新位置
for (int i = 0; i < no; i++) { // 添加no个"a"
result[j++] = 'a';
}
// 添加空格分隔单词
result[j++] = ' ';
*pos = j; // 更新结果字符串的当前写入位置
}
char* toGoatLatin(char* sentence) {
int len = strlen(sentence); // 获取句子长度
// 为结果字符串分配内存
char* result = (char *)(calloc(150, sizeof(char))); // 150是预估的足够大的空间
if (!result) {
return NULL; // 内存分配失败
}
// 分配临时字符串存储每个单词
char* temp = (char *)(calloc(150, sizeof(char)));
if (!temp) {
free(result); // 释放结果字符串的内存
return NULL; // 内存分配失败
}
int pos = 0; // 当前结果字符串的写入位置
int no = 0; // 当前单词的序号
// 使用strtok拆分句子为单词
char* token = strtok(sentence, " ");
while (token != NULL) {
no++; // 增加单词序号
strcpy(temp, token); // 将当前单词拷贝到临时字符串
process(temp, result, &pos, no); // 处理单词并写入结果
token = strtok(NULL, " "); // 获取下一个单词
}
// 移除末尾的空格
if (pos > 0) {
result[pos - 1] = '\0'; // 用'\0'替换最后一个空格
}
free(temp); // 释放临时字符串的内存
return result; // 返回结果
}
int main() {
char sentence[] = "I speak Goat Latin"; // 定义输入句子
char* ans = toGoatLatin(sentence);
puts(ans); // 输出结果
return 0;
}
strtok
函数是 C 标准库中的一个字符串处理函数,主要用于将字符串分割成一系列的子字符串,这些子字符串由指定的分隔符来划分。它的全名是 "string token",即字符串标记。
cppchar *strtok(char *str, const char *delim);
str
:指向要分割的字符串的指针。在第一次调用时,str
应该指向待分割的字符串。在后续的调用中,str
应该是NULL
,这样strtok
会继续从上一次分割的位置开始处理。
delim
:指向分隔符字符串的指针,其中包含了用于分割字符串的所有字符。功能
strtok
函数会修改原始字符串,在每个分隔符的位置插入空字符\0
,从而将字符串分割成多个子字符串。每次调用strtok
都会返回下一个标记(token)的指针,直到没有更多的标记为止,此时返回NULL
。
cpp#include <stdio.h> #include <string.h> int main() { char str[] = "Hello,world,this,is,a,test"; char *token; token = strtok(str, ","); while (token != NULL) { printf("%s\n", token); token = strtok(NULL, ","); } return 0; }
工作原理
第一次调用:
strtok
接受字符串和分隔符作为参数。它会在字符串中查找第一个分隔符,并在其位置插入
\0
,从而分割出第一个标记。返回第一个标记的指针。
后续调用:
将
str
参数设置为NULL
。
strtok
会继续从上次分割的位置开始查找下一个分隔符,并进行类似的处理。重复这个过程,直到没有更多的标记。
83. 删除排序链表中的重复元素
给定一个已排序的链表的头 head
, 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
示例 1:

输入:head = [1,1,2]
输出:[1,2]
示例 2:

输入:head = [1,1,2,3,3]
输出:[1,2,3]
提示:
- 链表中节点数目在范围
[0, 300]
内 -100 <= Node.val <= 100
- 题目数据保证链表已经按升序 排列
cpp
#include <cstdio> // 包含输入输出流头文件
#include <cstdlib> // 包含标准库函数头文件
#include <cstring> // 包含字符串处理函数头文件
#include <cctype> // 包含字符处理函数头文件
// 定义链表节点结构体
struct ListNode {
int val; // 节点值
struct ListNode *next; // 指向下一个节点的指针
};
// 初始化链表函数
void init(ListNode** L) {
*L = NULL; // 将链表初始化为空(头指针置为NULL)
}
// 创建链表函数(尾插法)
void create(ListNode** L, int num) {
struct ListNode* temp = NULL; // 临时指针,用于创建新节点
struct ListNode* tail = NULL; // 尾指针,用于跟踪链表尾部
int i = 0, e; // 循环计数器和输入值
// 使用尾插法创建链表
while (i < num) {
// 动态分配新节点
temp = (struct ListNode*)malloc(sizeof(struct ListNode));
if (temp == NULL) {
printf("内存分配失败!\n");
exit(1); // 内存分配失败时退出程序
}
// 提示用户输入第i+1个元素的值
printf("input the No.%d: ", i + 1);
scanf("%d", &e); // 读取用户输入
// 初始化新节点
temp->val = e; // 设置节点值
temp->next = NULL; // 新节点的next指针初始化为NULL
// 如果链表为空(头指针为NULL),则将新节点作为头节点
if (*L == NULL) {
*L = temp; // 头指针指向新节点
tail = temp; // 尾指针也指向新节点
} else {
tail->next = temp; // 将新节点链接到链表末尾
tail = temp; // 更新尾指针
}
i++; // 循环计数器加1
}
}
// 删除链表中重复节点函数
struct ListNode* deleteDuplicates(struct ListNode* head) {
if (head == NULL) {
return head; // 如果链表为空,直接返回
}
struct ListNode* p = head; // 指向当前节点的指针
struct ListNode* q = p->next; // 指向当前节点的下一个节点的指针
struct ListNode* temp; // 临时指针,用于保存被删除的节点
// 遍历链表
while (q) {
if (q->val == p->val) {
// 如果当前节点值等于前一节点值,则删除当前节点
temp = q; // 保存当前节点
p->next = q->next; // 前一节点的next指向当前节点的下一个节点
q = p->next; // 当前节点移动到下一个节点
free(temp); // 释放被删除节点的内存
} else {
// 如果当前节点值不等于前一节点值,继续遍历
p = p->next; // 前一节点移动到当前节点
q = p->next; // 当前节点移动到下一个节点
}
}
return head; // 返回处理后的链表头节点
}
int main() {
struct ListNode* L; // 定义链表头指针
init(&L); // 初始化链表
int num = 0;
printf("how many do you want to create?"); // 提示用户输入链表元素数量
scanf("%d", &num); // 读取用户输入的链表元素数量
create(&L, num); // 创建链表
// 删除链表中的重复节点
struct ListNode* head = deleteDuplicates(L);
struct ListNode* p = head; // 定义指针用于遍历链表
// 输出处理后的链表
while (p) {
printf("%d ", p->val); // 打印节点值
p = p->next; // 移动到下一个节点
}
return 0;
}
657. 机器人能否返回原点
在二维平面上,有一个机器人从原点 (0, 0)
开始。给出它的移动顺序,判断这个机器人在完成移动后是否在**(0, 0)
处结束**。
移动顺序由字符串 moves
表示。字符 move[i]
表示其第 i
次移动。机器人的有效动作有 R
(右),L
(左),U
(上)和 D
(下)。
如果机器人在完成所有动作后返回原点,则返回 true
。否则,返回 false
。
注意: 机器人"面朝"的方向无关紧要。 "R"
将始终使机器人向右移动一次,"L"
将始终向左移动等。此外,假设每次移动机器人的移动幅度相同。
示例 1:
输入: moves = "UD"
输出: true
解释:机器人向上移动一次,然后向下移动一次。所有动作都具有相同的幅度,因此它最终回到它开始的原点。因此,我们返回 true。
示例 2:
输入: moves = "LL"
输出: false
解释:机器人向左移动两次。它最终位于原点的左侧,距原点有两次 “移动” 的距离。我们返回 false,因为它在移动结束时没有返回原点。
提示:
1 <= moves.length <= 2 * 104
moves
只包含字符'U'
,'D'
,'L'
和'R'
cpp
#include <cstdio> // 包含输入输出流头文件
bool judgeCircle(char* moves) {
int x = 0,y = 0;
int i = 0;
while (moves[i] != '\0'){
switch (moves[i]) {
case 'U':
y++;
break;
case 'D':
y--;
break;
case 'R':
x++;
break;
case 'L':
x--;
break;
}
i++;
}
return x==0 && y==0;
}
int main() {
char* s = "UD";
if (judgeCircle(s)){
puts("true");
} else{
puts("false");
}
return 0;
}
551. 学生出勤记录 I
给你一个字符串 s
表示一个学生的出勤记录,其中的每个字符用来标记当天的出勤情况(缺勤、迟到、到场)。记录中只含下面三种字符:
'A'
:Absent,缺勤'L'
:Late,迟到'P'
:Present,到场
如果学生能够 同时 满足下面两个条件,则可以获得出勤奖励:
- 按 总出勤 计,学生缺勤(
'A'
)严格 少于两天。 - 学生 不会 存在 连续 3 天或 连续 3 天以上的迟到(
'L'
)记录。
如果学生可以获得出勤奖励,返回 true
;否则,返回 false
。
示例 1:
输入:s = "PPALLP"
输出:true
解释:学生缺勤次数少于 2 次,且不存在 3 天或以上的连续迟到记录。
示例 2:
输入:s = "PPALLL"
输出:false
解释:学生最后三天连续迟到,所以不满足出勤奖励的条件。
提示:
1 <= s.length <= 1000
s[i]
为'A'
、'L'
或'P'
cpp
#include <cstdio> // 包含输入输出流头文件
/**
* checkRecord函数检查一个员工的考勤记录是否满足以下条件:
* 1. 缺席('A')不超过一次。
* 2. 连续迟到('L')不超过两次。
*
* @param s 考勤记录字符串。
* @return 如果满足条件返回true,否则返回false。
*/
bool checkRecord(const char* s) {
int a = 0; // 统计缺席('A')的次数
int l = 0; // 统计连续迟到('L')的次数
int i = 0; // 字符串索引
while (s[i] != '\0') { // 遍历字符串直到结束符
if (s[i] == 'A') {
a++; // 遇到'P'时,缺席次数加1
l = 0; // 超过三个连续L或者A出现的时候,将连续L数目置0
} else if (s[i] == 'P') {
l = 0; // 遇到'P'时,重置连续迟到次数
} else if (s[i] == 'L') {
l++; // 遇到'L'时,连续迟到次数加1
if (l >= 3) {
return false; // 如果连续迟到次数达到3次,则立即返回false
}
}
i++;
}
return a < 2; // 最终检查缺席次数是否小于2次
}
int main() {
char* s = "PPALLL"; // 定义一个考勤记录字符串
if (checkRecord(s)) {
puts("true"); // 如果满足条件,输出true
} else {
puts("false"); // 否则输出false
}
return 0;
}
482. 密钥格式化
给定一个许可密钥字符串 s
,仅由字母、数字字符和破折号组成。字符串由 n
个破折号分成 n + 1
组。你也会得到一个整数 k
。
我们想要重新格式化字符串 s
,使每一组包含 k
个字符,除了第一组,它可以比 k
短,但仍然必须包含至少一个字符。此外,两组之间必须插入破折号,并且应该将所有小写字母转换为大写字母。
返回 重新格式化的许可密钥 。
示例 1:
输入:S = "5F3Z-2e-9-w", k = 4
输出:"5F3Z-2E9W"
解释:字符串 S 被分成了两个部分,每部分 4 个字符;
注意,两个额外的破折号需要删掉。
示例 2:
输入:S = "2-5g-3-J", k = 2
输出:"2-5G-3J"
解释:字符串 S 被分成了 3 个部分,按照前面的规则描述,第一部分的字符可以少于给定的数量,其余部分皆为 2 个字符。
提示:
1 <= s.length <= 10的5次方
s
只包含字母、数字和破折号'-'
.1 <= k <= 10的4次方
cpp
#include <stdio.h> // 包含输入输出流头文件
#include <stdlib.h> // 包含标准库函数头文件
#include <string.h> // 包含字符串处理函数头文件
#include <ctype.h> // 包含字符处理函数头文件
char* licenseKeyFormatting(char* s, int k) {
// 计算有效字符(非'-')的数量
int len = 0;
for (int i = 0; s[i] != '\0'; i++) {
if (s[i] != '-') len++;
}
// 如果没有有效字符,直接返回空字符串
if (len == 0) {
char* result = (char*)calloc(1, sizeof(char));
return result;
}
// 计算第一组的长度(可能不足k个字符)
int firstGroup = len % k;
if (firstGroup == 0) firstGroup = k; // 如果整除,则第一组长度取k
// 计算总长度:有效字符数 + 破折号的数量
int totalLen = len + (len / k);
if (firstGroup != k) totalLen++; // 如果第一组不是完整的k个,增加一个破折号
// 分配结果字符串的内存(加1是为了存储结束符)
char* result = (char*)calloc(totalLen + 1, sizeof(char));
// 用于跟踪结果字符串的当前写入位置
int indexOfResult = 0;
// 移除破折号并将字符转换为大写
char* temp = (char*)calloc(len + 1, sizeof(char));
int tempIndex = 0;
for (int i = 0; s[i] != '\0'; i++) {
if (s[i] != '-') {
temp[tempIndex++] = toupper(s[i]); // 转换为大写
}
}
temp[tempIndex] = '\0'; // 结束字符串
// 处理第一组字符
if (firstGroup != 0) {
// 拷贝第一组字符到结果
for (int i = 0; i < firstGroup; i++) {
result[indexOfResult++] = temp[i];
}
if (len - firstGroup > 0) {
result[indexOfResult++] = '-'; // 添加破折号
}
}
// 处理剩余的组
for (int i = firstGroup; i < len; i += k) {
if (i > firstGroup) { // 除了第一组外的其他组前加破折号
result[indexOfResult++] = '-';
}
// 拷贝k个字符到结果(不足k个时处理到字符串末尾)
for (int j = 0; j < k && i + j < len; j++) {
result[indexOfResult++] = temp[i + j];
}
}
result[indexOfResult] = '\0'; // 结束结果字符串
free(temp); // 释放临时字符串的内存
return result;
}
int main() {
char s[] = "---"; // 定义输入字符串
char* ans = licenseKeyFormatting(s, 3); // 调用格式化函数
puts(ans); // 输出结果
// 注意:需要手动释放ans的内存,以避免内存泄漏
free(ans);
return 0;
}
28. 找出字符串中第一个匹配项的下标
给你两个字符串 haystack
和 needle
,请你在 haystack
字符串中找出 needle
字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle
不是 haystack
的一部分,则返回 -1
。
示例 1:
输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。
示例 2:
输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。
提示:
1 <= haystack.length, needle.length <= 10的4次方
haystack
和needle
仅由小写英文字符组成
cpp
#include <cstdio> // 引入标准输入输出库
#include <cstring> // 引入字符串操作库,用于 strlen 等函数
#include <cstdlib> // 引入<stdlib.h>,用于 calloc 等内存分配函数
// 函数:计算模式串的前缀函数(Longest Prefix Suffix - LPS 数组)
void computeLPSArray(char* pattern, int m, int* lsp) {
int length = 0; // length 表示当前最长前缀后缀的长度,初始化为 0
lsp[0] = 0; // LPS 数组的第一个元素始终为 0,因为没有前缀和后缀
int i = 1; // 从模式串的第二个字符开始处理
while (i < m) {
if (pattern[i] == pattern[length]) {
// 如果当前字符与前缀字符匹配,则延长前缀长度
length++;
lsp[i] = length; // 更新 LPS 数组对应位置的值为当前 length
i++; // 移动到下一个字符
} else {
// 如果不匹配,根据前缀函数回退 length
if (length != 0) {
// 若 length 不为 0,回退到前一个匹配位置
length = lsp[length - 1];
} else {
// 如果 length 为 0,说明无法匹配更长的前缀,将 LPS 数组对应位置设为 0
lsp[i] = 0;
i++; // 移动到下一个字符
}
}
}
}
// 函数:实现基于 KMP 算法的字符串匹配
int strStr(char* haystack, char* needle) {
int n = strlen(haystack); // 获取主串的长度
int m = strlen(needle); // 获取模式串的长度
// 如果模式串长度大于主串长度,直接返回 -1
if (n < m) {
return -1;
}
// 为模式串的 LPS 数组分配足够的内存
int* lsp = (int*)calloc(m, sizeof(int));
computeLPSArray(needle, m, lsp); // 计算模式串的 LPS 数组
int i = 0, j = 0; // i 指向主串的当前位置,j 指向模式串的当前位置
while (i < n) { // 遍历主串
if (haystack[i] == needle[j]) {
// 如果当前字符匹配,同时移动主串和模式串的指针
j++;
i++;
}
if (j == m) {
// 当 j 达到模式串长度时,说明找到匹配子串
free(lsp); // 释放 LPS 数组的内存
return i - m; // 返回匹配子串的起始位置
} else if (i < n && haystack[i] != needle[j]) {
// 如果字符不匹配,且主串指针未越界
if (j != 0) {
// 如果模式串指针 j 不为 0,根据 LPS 数组回退 j
j = lsp[j - 1];
} else {
// 如果 j 为 0,无法回退,只能移动主串指针 i 以继续查找
i++;
}
}
}
// 如果循环结束仍未找到匹配,释放 LPS 数组的内存并返回 -1
free(lsp);
return -1;
}
int main() {
char haystack[] = "mississippi"; // 主字符串
char needle[] = "issip"; // 模式串
// 调用 strStr 函数查找模式串在主串中的位置,并输出结果
printf("%d", strStr(haystack, needle)); // 输出结果为 4
return 0;
}
KMP算法:最浅显易懂的 KMP 算法讲解_哔哩哔哩_bilibili
1、初始化LPS数组:
- 创建一个长度为模式串长度的数组
lsp
,用于存储每个位置的最长前缀后缀长度。- 初始化
lsp[0] = 0
,因为第一个字符没有前缀和后缀。2、遍历模式串:
- 从模式串的第二个字符开始(
i = 1
),逐个字符检查。3、处理每个字符:
- i=1 ,字符's':
- 当前最长前缀长度
length=0
。- 比较
pattern[1]
('s')与pattern[0]
('i'),不匹配。- 因此,
lsp[1]=0
,length
保持0,i
增加到2。- i=2 ,字符's':
length=0
。- 比较
pattern[2]
('s')与pattern[0]
('i'),不匹配。lsp[2]=0
,i
增加到3。
- i=3 ,字符'i':
length=0
。- 比较
pattern[3]
('i')与pattern[0]
('i'),匹配。length
增加到1,lsp[3]=1
,i
增加到4。
- i=4 ,字符'p':
length=1
。- 比较
pattern[4]
('p')与pattern[1]
('s'),不匹配。- 因为
length
不为0,将length
更新为lsp[length-1]
,即lsp[0]=0
。- 现在
length=0
,继续比较pattern[4]
('p')与pattern[0]
('i'),不匹配。lsp[4]=0
,i
增加到5,结束循环。
还是暴力破解吧!
cpp
#include <cstdio> // 引入标准输入输出库
#include <cstring> // 引入字符串操作库,用于 strlen 等函数
// 函数:查找子串在主串中的位置(暴力匹配)
int strStr(char* haystack, char* needle) {
int n = strlen(haystack); // 获取主串的长度
int m = strlen(needle); // 获取子串的长度
// 遍历主串中所有可能的起始位置
for (int i = 0; i + m <= n; i++) {
bool flag = true; // 标记是否找到匹配的子串
// 逐个字符比较当前起始位置的子串与 needle
for (int j = 0; j < m; j++) {
if (haystack[i + j] != needle[j]) {
flag = false; // 如果发现不匹配的字符,标记为 false
break; // 退出当前子串的比较
}
}
// 如果 flag 为 true,说明找到匹配的子串
if (flag) {
return i; // 返回子串的起始位置
}
}
// 如果循环结束仍未找到匹配的子串,返回 -1
return -1;
}
int main() {
char haystack[] = "mississippi"; // 主字符串
char needle[] = "issip"; // 子串
// 调用 strStr 函数查找子串在主串中的位置,并输出结果
printf("%d", strStr(haystack, needle)); // 输出结果为 4
return 0;
}