LeetCode刷题笔记(C语言)
题一:给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。 (例如,"ace"是"abcde"的一个子序列,而"aec"不是)
• 0 <= s.length <= 100
• 0 <= t.length <= 10^4
• 两个字符串都只由小写字符组成。
方法一:使用贪心算法进行走一步算一步的层层递进、逐个击破的方法达成最终目标
贪心算法(Greedy Algorithm) 简介:
贪心算法,又名贪婪法,是寻找最优解问题的常用方法,这种方法模式一般将求解过程分成若干个步骤,但每个步骤都应用贪心原则,选取当前状态下最好/最优的选择(局部最有利的选择),并以此希望最后堆叠出的结果也是最好/最优的解。{看着这个名字,贪心,贪婪这两字的内在含义最为关键。这就好像一个贪婪的人,他事事都想要眼前看到最好的那个,看不到长远的东西,也不为最终的结果和将来着想,贪图眼前局部的利益最大化,有点走一步看一步的感觉。}
链接:推荐一篇关于贪心算法的博客
程序实现:
1. bool isSubsequence(char * s, char * t){
2. /*
3. * i:用于字符串t的遍历
4. * n:用于字符串s的当前字符位置
5. */
6. int i,n;
7. i = n = 0; //从0开始
8. if(strlen(s) > strlen(t)) return false; //如果字符串s长度比字符串t长度要大的话,s肯定不是t的子序列
9. if(strlen(s) == 0) return true; //s字符串长度为0,说明字符串元素被删除完了,肯定是t的子序列
10. /* 贪心算法,遍历整个字符串t,一个一个的比较字符串s内的字符不否符合字符串t的子序列字符之一 */
11. while(i < strlen(t)) {
12. /* 比较当前字符串t当前字符与字符串s当前字符相同 */
13. if(*(s+n) == *(t+i)) {
14. n += 1;
15. /* 如果已经把字符串s的都对比完了,那么说明字符串s是字符串t的子序列,此时就可以停止遍历字符串t了 */
16. if(strlen(s) == n) return true;
17. }
18. i += 1;
19. }
20. return false;
21. }
方法二:动态规划
下图为力扣官方题解的对动态规划的描述。
动态规划简单来说好比做了一张模板卡,然后别的工艺品做出来后就直接那模板卡对比,省去了那些复杂的测量步骤,也就是说减少重复做的事情,把复杂的事情尽量简单化。具体的讲解,大家可以自行搜索百度。
步骤一:建表
建立边界,此边界除了作为表格的边界外,边界元素的值还是用于录表中标注字母在某顺序位置未出现或无。
程序实现:
8. /* 1、建表 */
9. for(int i=0;i<26;i++) {
10. table_str[m][i] = m; //在表低添加无效元素值作为边界
11. }
步骤二:录表
按英文字母顺序和字符位置反顺序进行录表。字符串的当前字符与英文字母横坐标对应的位置(纵坐标字符位置)设置元素值为该位置是出现字符串t的第几(i)个字符,这目的是为了到时查表的时候就知道子序列s的当前字符是字符串t的第几个字符;而不符合的则标注为与之对应上下一个字符位置的元素值,这样可以证明该位置出现在第几个字符位置后或者无效元素。
程序实现:
13. /* 2、录表 */
14. for(int i=m-1;i>=0;i--) { //字符串t反向顺序录入元素值
15. for(int j=0;j<26;j++) { //英文字母元素值对应坐标轮询
16. if(t[i]==(j+'a')) { //比较当前字符串t元素值是否与当前英文字符是对应关系
17. table_str[i][j] = i; //如果上面判断成立,标注表的当前位置元素值为当前字符串t元素的位置顺序
18. } else {
19. table_str[i][j] = table_str[i+1][j]; //如不成立,标注为当前位置的纵向下一个位置的元素值,可能是字符串t某个元素出现的顺序值,也可能是边界元素值(无出现的元素)
20. }
21. }
22. }
步骤三:查表
此图是我截取官方视频讲解的。
根据子序列s当前字符元素与横坐标英文字符相比较,找出与之对应的,然后判断该字符位置(纵坐标)的表格元素是否是字符串t的有效字符顺序(例如图示0->5,而6是无效顺序),然后读取子序列s当前字符在字符串t出现的位置顺序值,将此值加一,读取下一个与子序列s下一个元素相对应的表格元素,重复上面步骤,直至读取完毕。
程序实现:
24. /* 3、查表,对比 */
25. for(int i=0;i<n;i++) {
26. if (table_str[current_num][s[i]-'a']==m) { //判断与之对应的元素值是否为边界元素值
27. return false;
28. }
29. current_num = table_str[current_num][s[i]-'a'] + 1; //下一个子序列s元素查询
30. }
方法二的完整程序:
1. bool isSubsequence(char* s, char* t) {
2. int n = strlen(s), m = strlen(t); //n:子序列s长度, m:字符串t长度
3. int current_num = 0; //轮询子序列s的当前元素位置
4. int table_str[m+1][26]; //表格
5. memset(table_str,0,sizeof(table_str)); //清空表格
6. /* 使用动态规划方法解题 */
7.
8. /* 1、建表 */
9. for(int i=0;i<26;i++) {
10. table_str[m][i] = m; //在表低添加无效元素值作为边界
11. }
12.
13. /* 2、录表 */
14. for(int i=m-1;i>=0;i--) { //字符串t反向顺序录入元素值
15. for(int j=0;j<26;j++) { //英文字母元素值对应坐标轮询
16. if(t[i]==(j+'a')) { //比较当前字符串t元素值是否与当前英文字符是对应关系
17. table_str[i][j] = i; //如果上面判断成立,标注表的当前位置元素值为当前字符串t元素的位置顺序
18. } else {
19. table_str[i][j] = table_str[i+1][j]; //如不成立,标注为当前位置的纵向下一个位置的元素值,可能是字符串t某个元素出现的顺序值,也可能是边界元素值(无出现的元素)
20. }
21. }
22. }
23.
24. /* 3、查表,对比 */
25. for(int i=0;i<n;i++) {
26. if (table_str[current_num][s[i]-'a']==m) { //判断与之对应的元素值是否为边界元素值
27. return false;
28. }
29. current_num = table_str[current_num][s[i]-'a'] + 1; //下一个子序列s元素查询
30. }
31.
32. return true;
33. }