[算法总结] 13 道题搞定 BAT 面试——字符串

本文首发于我的个人博客:尾尾部落

1. KMP 算法

谈到字符串问题,不得不提的就是 KMP 算法,它是用来解决字符串查找的问题,可以在一个字符串(S)中查找一个子串(W)出现的位置。KMP 算法把字符匹配的时间复杂度缩小到 O(m+n) ,而空间复杂度也只有O(m)。因为“暴力搜索”的方法会反复回溯主串,导致效率低下,而KMP算法可以利用已经部分匹配这个有效信息,保持主串上的指针不回溯,通过修改子串的指针,让模式串尽量地移动到有效的位置。

具体算法细节请参考:

  • 字符串匹配的KMP算法
  • 从头到尾彻底理解KMP
  • 如何更好的理解和掌握 KMP 算法?
  • KMP 算法详细解析
  • 图解 KMP 算法
  • 汪都能听懂的KMP字符串匹配算法【双语字幕】
  • KMP字符串匹配算法1

1.1 BM 算法

BM算法也是一种精确字符串匹配算法,它采用从右向左比较的方法,同时应用到了两种启发式规则,即坏字符规则 和好后缀规则 ,来决定向右跳跃的距离。基本思路就是从右往左进行字符匹配,遇到不匹配的字符后从坏字符表和好后缀表找一个最大的右移值,将模式串右移继续匹配。 字符串匹配的KMP算法

2. 替换空格

剑指offer:替换空格 请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

public class Solution {public String replaceSpace(StringBuffer str) {StringBuffer res = new StringBuffer();int len = str.length() - 1;for(int i = len; i >= 0; i--){if(str.charAt(i) == ' ')res.append("02%");elseres.append(str.charAt(i));}return res.reverse().toString();}
}
复制代码

3. 最长公共前缀

Leetcode: 最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。

首先对字符串数组进行排序,然后拿数组中的第一个和最后一个字符串进行比较,从第 0 位开始,如果相同,把它加入 res 中,不同则退出。最后返回 res

class Solution {public String longestCommonPrefix(String[] strs) {if(strs == null || strs.length == 0)return "";Arrays.sort(strs);char [] first = strs[0].toCharArray();char [] last = strs[strs.length - 1].toCharArray();StringBuffer res = new StringBuffer();int len = first.length < last.length ? first.length : last.length;int i = 0;while(i < len){if(first[i] == last[i]){res.append(first[i]);i++;}elsebreak;}return res.toString();}
}
复制代码

4. 最长回文串

LeetCode: 最长回文串 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。

统计字母出现的次数即可,双数才能构成回文。因为允许中间一个数单独出现,比如“abcba”,所以如果最后有字母落单,总长度可以加 1。

class Solution {public int longestPalindrome(String s) {HashSet<Character> hs = new HashSet<>();int len = s.length();int count = 0;if(len == 0)return 0;for(int i = 0; i<len; i++){if(hs.contains(s.charAt(i))){hs.remove(s.charAt(i));count++;}else{hs.add(s.charAt(i));}}return hs.isEmpty() ? count * 2 : count * 2 + 1;}
}
复制代码

4.1 验证回文串

Leetcode: 验证回文串 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。 说明:本题中,我们将空字符串定义为有效的回文串。

两个指针比较头尾。要注意只考虑字母和数字字符,可以忽略字母的大小写。

class Solution {public boolean isPalindrome(String s) {if(s.length() == 0)return true;int l = 0, r = s.length() - 1;while(l < r){if(!Character.isLetterOrDigit(s.charAt(l))){l++;}else if(!Character.isLetterOrDigit(s.charAt(r))){r--;}else{if(Character.toLowerCase(s.charAt(l)) != Character.toLowerCase(s.charAt(r)))return false;l++;r--;} }return true;}
}
复制代码

4.2 最长回文子串

LeetCode: 最长回文子串 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。

以某个元素为中心,分别计算偶数长度的回文最大长度和奇数长度的回文最大长度。

class Solution {private int index, len;public String longestPalindrome(String s) {if(s.length() < 2)return s;for(int i = 0; i < s.length()-1; i++){PalindromeHelper(s, i, i);PalindromeHelper(s, i, i+1);}return s.substring(index, index+len);}public void PalindromeHelper(String s, int l, int r){while(l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)){l--;r++;}if(len < r - l - 1){index = l + 1;len = r - l - 1;}}
}
复制代码

4.3 最长回文子序列

LeetCode: 最长回文子序列 给定一个字符串s,找到其中最长的回文子序列。可以假设s的最大长度为1000。 最长回文子序列和上一题最长回文子串的区别是,子串是字符串中连续的一个序列,而子序列是字符串中保持相对位置的字符序列,例如,"bbbb"可以使字符串"bbbab"的子序列但不是子串。

动态规划: dp[i][j] = dp[i+1][j-1] + 2 if s.charAt(i) == s.charAt(j) otherwise, dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1])

class Solution {public int longestPalindromeSubseq(String s) {int len = s.length();int [][] dp = new int[len][len];for(int i = len - 1; i>=0; i--){dp[i][i] = 1;for(int j = i+1; j < len; j++){if(s.charAt(i) == s.charAt(j))dp[i][j] = dp[i+1][j-1] + 2;elsedp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);}}return dp[0][len-1];}
}
复制代码

5. 字符串的排列

Leetcode: 字符串的排列 给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。 换句话说,第一个字符串的排列之一是第二个字符串的子串。

我们不用真的去算出s1的全排列,只要统计字符出现的次数即可。可以使用一个哈希表配上双指针来做。

class Solution {public boolean checkInclusion(String s1, String s2) {int l1 = s1.length();int l2 = s2.length();int [] count = new int [128];if(l1 > l2)return false;for(int i = 0; i<l1; i++){count[s1.charAt(i) - 'a']++;count[s2.charAt(i) - 'a']--;}if(allZero(count))return true;for(int i = l1; i<l2; i++){count[s2.charAt(i) - 'a']--;count[s2.charAt(i-l1) - 'a']++;if(allZero(count))return true;}return false;}public boolean allZero(int [] count){int l = count.length;for(int i = 0; i < l; i++){if(count[i] != 0)return false;}return true;}
}
复制代码

6. 打印字符串的全排列

剑指offer:字符串的排列 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

把问题拆解成简单的步骤: 第一步求所有可能出现在第一个位置的字符(即把第一个字符和后面的所有字符交换[相同字符不交换]); 第二步固定第一个字符,求后面所有字符的排列。这时候又可以把后面的所有字符拆成两部分(第一个字符以及剩下的所有字符),依此类推。这样,我们就可以用递归的方法来解决。

public class Solution {ArrayList<String> res = new ArrayList<String>();public ArrayList<String> Permutation(String str) {if(str == null)return res;PermutationHelper(str.toCharArray(), 0);Collections.sort(res);return res;}public void PermutationHelper(char[] str, int i){if(i == str.length - 1){res.add(String.valueOf(str));}else{for(int j = i; j < str.length; j++){if(j!=i && str[i] == str[j])continue;swap(str, i, j);PermutationHelper(str, i+1);swap(str, i, j);}}}public void swap(char[] str, int i, int j) {char temp = str[i];str[i] = str[j];str[j] = temp;}
}
复制代码

7. 第一个只出现一次的字符

剑指offer: 第一个只出现一次的字符 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1.

先在hash表中统计各字母出现次数,第二次扫描直接访问hash表获得次数。也可以用数组代替hash表。

import java.util.HashMap;
public class Solution {public int FirstNotRepeatingChar(String str) {int len = str.length();if(len == 0)return -1;HashMap<Character, Integer> map = new HashMap<>();for(int i = 0; i < len; i++){if(map.containsKey(str.charAt(i))){int value = map.get(str.charAt(i));map.put(str.charAt(i), value+1);}else{map.put(str.charAt(i), 1);}}for(int i = 0; i < len; i++){if(map.get(str.charAt(i)) == 1)return i;}return -1;}
}
复制代码

8. 翻转单词顺序列

剑指offer: 翻转单词顺序列 LeetCode: 翻转字符串里的单词

借助trim()和 split()就很容易搞定

public class Solution {public String reverseWords(String s) {if(s.trim().length() == 0)return s.trim();String [] temp = s.trim().split(" +");String res = "";for(int i = temp.length - 1; i > 0; i--){res += temp[i] + " ";}return res + temp[0];}
}
复制代码

9. 旋转字符串

Leetcode: 旋转字符串 给定两个字符串, A 和 B。 A 的旋转操作就是将 A 最左边的字符移动到最右边。 例如, 若 A = 'abcde',在移动一次之后结果就是'bcdea' 。如果在若干次旋转操作之后,A 能变成B,那么返回True。

一行代码搞定

class Solution {public boolean rotateString(String A, String B) {return A.length() == B.length() && (A+A).contains(B);}
}
复制代码

9.1 左旋转字符串

剑指offer: 左旋转字符串 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

在第 n 个字符后面将切一刀,将字符串分为两部分,再重新并接起来即可。注意字符串长度为 0 的情况。

public class Solution {public String LeftRotateString(String str,int n) {int len = str.length();if(len == 0)return "";n = n % len;String s1 = str.substring(n, len);String s2 = str.substring(0, n);return s1+s2;}
}
复制代码

9.2 反转字符串

LeetCode: 反转字符串 编写一个函数,其作用是将输入的字符串反转过来。

class Solution {public String reverseString(String s) {if(s.length() < 2)return s;int l = 0, r = s.length() - 1;char [] strs = s.toCharArray(); while(l < r){char temp = strs[l];strs[l] = strs[r];strs[r] = temp;l++;r--;}return new String(strs);}
}
复制代码

10. 把字符串转换成整数

剑指offer: 把字符串转换成整数 将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。

public class Solution {public int StrToInt(String str) {if(str.length() == 0)return 0;int flag = 0;if(str.charAt(0) == '+')flag = 1;else if(str.charAt(0) == '-')flag = 2;int start = flag > 0 ? 1 : 0;long res = 0;while(start < str.length()){if(str.charAt(start) > '9' || str.charAt(start) < '0')return 0;res = res * 10 + (str.charAt(start) - '0');start ++;}return flag == 2 ? -(int)res : (int)res;}
}
复制代码

11. 正则表达式匹配

剑指offer:正则表达式匹配 请实现一个函数用来匹配包括’.’和’*’的正则表达式。模式中的字符’.’表示任意一个字符,而’*’表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”ab*ac*a”匹配,但是与”aa.a”和”ab*a”均不匹配

动态规划: 这里我们采用dp[i+1][j+1]代表s[0..i]匹配p[0..j]的结果,结果自然是采用布尔值True/False来表示。 首先,对边界进行赋值,显然dp[0][0] = true,两个空字符串的匹配结果自然为True; 接着,我们对dp[0][j+1]进行赋值,因为 i=0 是空串,如果一个空串和一个匹配串想要匹配成功,那么只有可能是p.charAt(j) == '*' && dp[0][j-1] 之后,就可以愉快地使用动态规划递推方程了。

public boolean isMatch(String s, String p) {if (s == null || p == null) {return false;}boolean[][] dp = new boolean[s.length()+1][p.length()+1];dp[0][0] = true;for (int j = 0; i < p.length(); j++) {if (p.charAt(j) == '*' && dp[0][j-1]) {dp[0][j+1] = true;}}for (int i = 0 ; i < s.length(); i++) {for (int j = 0; j < p.length(); j++) {if (p.charAt(j) == '.') {dp[i+1][j+1] = dp[i][j];}if (p.charAt(j) == s.charAt(i)) {dp[i+1][j+1] = dp[i][j];}if (p.charAt(j) == '*') {if (p.charAt(j-1) != s.charAt(i) && p.charAt(j-1) != '.') {dp[i+1][j+1] = dp[i+1][j-1];} else {dp[i+1][j+1] = (dp[i+1][j] || dp[i][j+1] || dp[i+1][j-1]);}}}}return dp[s.length()][p.length()];
}
复制代码

12. 表示数值的字符串

剑指offer: 表示数值的字符串 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串”+100″,”5e2″,”-123″,”3.1416″和”-1E-16″都表示数值。 但是”12e”,”1a3.14″,”1.2.3″,”+-5″和”12e+4.3″都不是。

设置三个标志符分别记录“+/-”、“e/E”和“.”是否出现过。

public class Solution {public boolean isNumeric(char[] str) {int len = str.length;boolean sign = false, decimal = false, hasE = false;for(int i = 0; i < len; i++){if(str[i] == '+' || str[i] == '-'){if(!sign && i > 0 && str[i-1] != 'e' && str[i-1] != 'E')return false;if(sign && str[i-1] != 'e' && str[i-1] != 'E')return false;sign = true;}else if(str[i] == 'e' || str[i] == 'E'){if(i == len - 1)return false;if(hasE)return false;hasE = true;}else if(str[i] == '.'){if(hasE || decimal)return false;decimal = true;}else if(str[i] < '0' || str[i] > '9')return false;}return true;}
}
复制代码

13. 字符流中第一个不重复的字符

剑指offer: 字符流中第一个不重复的字符 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符”go”时,第一个只出现一次的字符是”g”。当从该字符流中读出前六个字符“google”时,第一个只出现一次的字符是”l”。

用一个哈希表来存储每个字符及其出现的次数,另外用一个字符串 s 来保存字符流中字符的顺序。

import java.util.HashMap;
public class Solution {HashMap<Character, Integer> map = new HashMap<Character, Integer>();StringBuffer s = new StringBuffer();//Insert one char from stringstreampublic void Insert(char ch){s.append(ch);if(map.containsKey(ch)){map.put(ch, map.get(ch)+1);}else{map.put(ch, 1);}}//return the first appearence once char in current stringstreampublic char FirstAppearingOnce(){for(int i = 0; i < s.length(); i++){if(map.get(s.charAt(i)) == 1)return s.charAt(i);}return '#';}
}
复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537928.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sqlserver备份存储过程

查了网上找不到快速备份Sqlserver存储过程的方法&#xff0c;心里想&#xff0c;如果Sqlserver不自带这个功能&#xff0c;真是太low了。步骤1&#xff1a;打开存储过程文件夹步骤2&#xff1a;按 F7 键&#xff0c;打开“对象资源管理器详细信息”窗口步骤3&#xff1a;点击“…

仿拉钩app(一)---爬虫数据准备

工欲善其事必先利其器&#xff0c;准备做一个拉钩的app&#xff0c;但是没数据可怎么办&#xff0c;那就直接扒裤衩去爬吧 一般爬虫的思路为&#xff1a; 分析页面结构是否有接口模仿请求&#xff08;解决反爬的各种方式&#xff09;解析数据存储数据按照以上的思路&#xff0c…

小哼买书JAVA编写,04_小哼买书

现在来看一个具体的例子“小哼买书”(根据全国青少年信息学奥林匹克联赛 NOIP2006 普及组第一题改编),来实践一下 章所学的三种排序算法。Paste_Image.png小哼的学校要建立一个图书角,老师派小哼去找一些同学做调查,看看同学们都喜欢读哪些书。小哼让每个同学写出一个自己最想读…

[Err] 22007 - [SQL Server]从 nvarchar 数据类型到 datetime 数据类型的转换产生一个超出范围的值。

报错语句&#xff1a; cast(Replace(Replace(P.DeliverDate,.,-),/,-) as datetime)改为 cast(Replace(Replace(P.DeliverDate,.,-),/,-) as datetime2)使用 datetime2 代替 datetime

linux Postfix + dovecot + extmail + extman + mysql

配置环境&#xff1a;RHEL5.5 i386DNS MX[rootstation40 ~]# host -t MX tianyun.comtianyun.com mail is handled by 10 mail.tianyun.com.[rootstation40 ~]# [rootstation40 ~]# ping mail.tianyun.comPING mail.tianyun.com (192.168.0.2) 56(84) bytes of data.64 bytes f…

php 接口安全解决方案,php接口数据安全解决方案(一)

前言目的&#xff1a;1.实现前后端代码分离&#xff0c;分布式部署2.利用token替代session实现状态保持&#xff0c;token是有时效性的满足退出登录&#xff0c;token存入redis可以解决不同服务器之间session不同步的问题&#xff0c;满足分布式部署3.利用sign&#xff0c;前端…

Teamview连接Windows server问题

场景&#xff1a; 服务器在集团总部杭州&#xff0c;网管在集团宁波分公司&#xff0c;连接服务器通过内网远程桌面。过程&#xff1a; 网管给了tv的账号&#xff0c;密码。连接的时候一直连不上去。卡在“正在初始化连接参数”。后来网管不信&#xff0c;远程桌面了下&#xf…

nginx An attempt was made to access a socket in a way forbidden by its access permissions

在安装了 sqlserver2008 的win7 与 win2008 上启动 nginx&#xff0c;绑定80端口&#xff0c;报错&#xff1a; nginx An attempt was made to access a socket in a way forbidden by its access permissions查了百度&#xff0c;说修改注册表&#xff0c;但我的电脑上找不到文…

php codesniffer 代码规范,规范三:PHP_CodeSniffer 辅佐代码规范

>也可以参考此文&#xff1a;https://www.cnblogs.com/huangbx/p/php_codesniffer.html[TOC]我用的是wamp&#xff0c;环境是php7.0.23# (一)下载 pear打开http://pear.php.net/go-pear.phar&#xff0c;会显示代码&#xff0c;不用管他&#xff0c;直接copys复制到本地&…

php的cms是什么意思,phpcms是什么系统

什么是phpcms&#xff1f;Phpcms 是国内领先的网站内容管理系统&#xff0c;同时也是一个开源的PHP开发框架。Phpcms由内容模型、会员、问吧、专题、财务、订单、广告、邮件订阅、 短消息、自定义表单、全站搜索等20多个功能模块组成&#xff0c;内置新闻、图片、下载、信息、产…

【python】 time模块和datetime模块详解 【转】

一、time模块 time模块中时间表现的格式主要有三种&#xff1a; a、timestamp时间戳&#xff0c;时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量 b、struct_time时间元组&#xff0c;共有九个元素组。 c、format time 格式化时间&#xff0c;已格式化的结构使时间更…

spring boot Exception in Thread “main” java.lang.classNoFoundException

在客户测试环境部署&#xff0c;通过打包成jar&#xff0c;使用命令 nohup java -jar /usr/local/tomcat/shirencai/ct-peixun-provider.jar –spring.profiles.activestage > /usr/local/tomcat/shirencai/ct-peixun-provider-temp.txt & 报错后来排查以为是内存不够。…

php源码自动识别文本中的链接,自动加载识别文件Auto.php

用于本应用的控制器自动加载类设置&#xff0c;用法如同\CodeIgniter\Config\AutoloadConfig自动加载识别文件:dayrui/App/应用目录/Config/Auto.php语法格式&#xff1a;<?php // 自动加载识别文件return [/*** 命名空间映射关系*/psr4 > [],/*** 类名映射关系*/classm…

如何识别“答非所问”?使用gensim进行文本相似度计算

在文本处理中&#xff0c;比如商品评论挖掘&#xff0c;有时需要了解每个评论分别和商品的描述之间的相似度&#xff0c;以此衡量评论的客观性。 评论和商品描述的相似度越高&#xff0c;说明评论的用语比较官方&#xff0c;不带太多感情色彩&#xff0c;比较注重描述商品的属性…

防抓包重放php,超简单最基本的WEB抓包改包重放的方法

【注意&#xff1a;此文章为博主原创文章&#xff01;转载需注意&#xff0c;请带原文链接&#xff0c;至少也要是txt格式&#xff01;】很多很多刚刚接触的同事问我如何抓包&#xff0c;如果讲用工具可能还涉及什么装证书&#xff0c;熟悉使用工具等等&#xff0c;特别繁琐&am…

mysql查询很慢优化方法1

解决方法&#xff1a; 关联的字段建索引。 具体分析如下&#xff1a;举例&#xff1a; 表格&#xff1a;培训学生表&#xff0c;班级报名表 需求&#xff1a;查询出学生报了哪些班级 两表有个关联字段“CD”&#xff08;学生学号&#xff09;。 视图sql&#xff1a; SELECTt_px…

ubuntu进行apt-get时候出现Package ssh is not available, but is referred to by another package 错误...

今天在ubuntu进行ssh安装的时候&#xff0c;出现如下错误。Reading package lists... Done Building dependency tree... Done Package ssh is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or is …

php找出函数定义位置,WordPress如何快速定位PHP函数所在文件位置及代码行号?

有时候我们需要修改别人源码里的代码&#xff0c;却找不到对应的函数放在了哪儿&#xff0c;就可以用使用本文介绍的办法&#xff0c;帮你快速定位函数位置。特别是某些写法不规范的WordPress主题&#xff0c;各种模块&#xff0c;函数到处放&#xff0c;找半天的那种。那么Wor…

微信公众号每次调用接口正确或错误的返回码

原文连接&#xff1a;https://blog.csdn.net/pansanday/article/details/65448868 ----------------------------------------- 公众号每次调用接口时&#xff0c;可能获得正确或错误的返回码&#xff0c;开发者可以根据返回码信息调试接口&#xff0c;排查错误。 全局返回码…

Phoenix:全局索引设计实践

概述 全局索引是Phoenix的重要特性&#xff0c;合理的使用二级索引能降低查询延时&#xff0c;让集群资源得以充分利用。 本文将讲述如何高效的设计和使用索引。 全局索引说明 全局索引的根本是通过单独的HBase表来存储数据表的索引数据。我们通过如下示例看索引数据和主表数据…