面试算法题 专题 —— 字符串
Posted Johnny*
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试算法题 专题 —— 字符串相关的知识,希望对你有一定的参考价值。
面试题 01.09. 字符串轮转
【题目描述】
解法一 模拟旋转
【思路】
指向s1的指针从前往后遍历逐一与s2的头一个字符比较 ,相等位置即为可能的旋转位置。旋转词极可能为: c[i + 1: n -1] + c[0:i]
class Solution {
public boolean isFlipedString(String s1, String s2) {
if( s1 == null || s2 == null) return false;
if( s1.equals(s2) ) return true;
char c [] = s1.toCharArray();
char g [] = s2.toCharArray();
int n = c.length, m = g.length;
if( n != m ) return false;
for(int i = 0, j = 0; i < n; i ++){
if( c[i] == g[j]){
String s = s1.substring(i) + s1.substring(0, i) ;
if( s.equals(s2)) return true;
}
}
return false;
}
}
解法二 : s1一定是 s2 + s2的子串
【思路】
如果s2是s1旋转得到的 那么s1一定是s的子串
class Solution {
public boolean isFlipedString(String s1, String s2) {
if( s1 == null || s2 == null) return false;
if( s1.length() != s2.length() ) return false;
String s = s2 + s2; //如果s2是s1旋转得到的 那么s1一定是s的子串
if( s.contains(s1)) return true;
return false;
}
}
572. 另一个树的子树
【题目描述】
解法一 : 递归判断
【思路】
层次遍历以s为根节点的树,也就是枚举出s的每一棵子树。判断以s为根的树与 以t为根的是否完全相同
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
/**
判断判断以root为根的树 和 以t为根的树是否相同
*/
public boolean sameTree(TreeNode root, TreeNode t ){
//t和root 都为null 已经匹配到叶子结点了 说明被被完全匹配上
if( t == null && root == null) return true;
//root和t中有一棵没被遍完,另一棵树被遍历完了
//或该两棵树对应节点值不相同。说明没匹配上
if( root == null || t == null ) return false;
if( root.val != t.val ) return false;
return sameTree(root.left, t.left) && sameTree(root.right, t.right);
}
public boolean isSubtree(TreeNode s, TreeNode t) {
//t为null 则一定匹配
if( t == null ) return true;
//s为null 则一定不匹配
if( s == null ) return false;
//判断以s为根的树与 以t为根的是否完全相同
if( sameTree(s, t) ) return true;
return isSubtree(s.left, t) || isSubtree(s.right, t);
}
}
解法二 : 序列化二叉树
【思路】
使用后序遍历序列化二叉树。
空节点对应字符串: !#
非空节点对应对应字符串 : 值!
这样才能保证序列化后的字符串唯一对应一棵二叉树。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if( s == null || t == null) return false;
return postSerialize(s).contains( postSerialize(t) );
}
public String postSerialize(TreeNode s){
StringBuilder sb = new StringBuilder();
post(s, sb);
return sb.toString();
}
//后序遍历二叉树
public void post(TreeNode s, StringBuilder sb){
//左孩子为空节点
if( s.left == null) sb.append("#!");
else post(s.left, sb);
//右孩子为空节点
if( s.right == null) sb.append("#!");
else post(s.right, sb);
//添加根节点
sb.append(s.val).append("!");
}
}
二叉树序列化与反序列化
【题目描述】
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
// Encodes a tree to a single string.
int u = 0;
String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
dfs_s(root, sb);
return sb.toString();
}
/**
* 前序遍历方式序列化二叉树 每个节点值以空格隔开
* 根左右
*/
void dfs_s(TreeNode root, StringBuilder res){
if( root == null ){
res.append("null").append(' ');
return;
}
res.append(root.val).append(' ');
dfs_s(root.left, res);
dfs_s(root.right, res);
}
/**
*反序列化
*/
// Decodes your encoded data to tree.
TreeNode deserialize(String data) {
char c[] = data.toCharArray();
TreeNode root = dfs_d(c);
return root;
}
TreeNode dfs_d(char c[]){
//u 是全局变量
if( u == c.length ) return null;
int k = u;
//寻找节点(有值节点和空节点)
while( c[k] != ' ') k ++;
//空节点
if(c[u] == 'n'){
u = k + 1; //指向下一个节点
return null;
}
//有值节点值为c[u : k)
int val = 0, sign = 1;
if( u < k && c[u] == '-'){
sign = -1;
u ++; //跳过 符号位
}
for(int i = u; i < k ; i ++)
val = val * 10 + (c[i] - '0');
val *= sign;
u = k + 1;
//构建二叉树
TreeNode root = new TreeNode(val);
root.left = dfs_d(c);
root.right = dfs_d (c);
return root;
}
}
有效的字母异位词
【题目描述】
Leetcode 242. 有效的字母异位词
【思路】
互为异位词的单词,只有字母放置的位置不同,两个字符串中字母出现的次数及种类都是相同的。
题目中 说明了,单词全是小写字母,所以只需开个长度为26的数组,统计在s中出现字母的次数。然后再遍历t,t中出现的字符对应数组位置元素减1,如果最后数组不全为0说明不是异位词。
class Solution {
public boolean isAnagram(String s, String t) {
//注意题目说明只包含小写字母
if( s == null && t == null ) return true;
if( s == null || t == null ) return false;
int n = s.length(), m = t.length();
if( n != m ) return false;
//异位词 即只有位置不同 字母出现的个数和出现的字母都相同
int [] cnt = new int[26];
for(int i = 0; i < n; i ++) {
cnt[ s.charAt(i) - 'a'] ++;
cnt[ t.charAt(i) - 'a'] --;
}
//判断是否有剩余
for(int i = 0; i < 26; i ++)
if( cnt[i] != 0) return false;
return true;
}
}
翻转字符串里的单词
解法一: 使用API
class Solution {
//使用API方法
public String reverseWords(String data) {
//trim去除字符串首尾的空格
String [] s = data.trim().split("\\\\s+");
// \\\\s 是匹配多个空格
int n = s.length;
//然后从后往前逐一将字符串加入新的字符串中
StringBuilder sb = new StringBuilder();
for(int i = n -1; i > 0; i --)
sb.append(s[i]).append(" ");
sb.append(s[0]);
return sb.toString();
}
}
解法二 :原地翻转
class Solution {
/**
翻转[l,r)区间的字符
*/
public void reverse(char c[], int l, int r){
//r--至右边第一个字符
r --;
while( l < r){
char t = c[l];
c[l] = c[r];
c[r] = t;
l ++;
r --;
}
}
public String reverseWords(String s) {
if( s == null) return null;
//s.trim去除首尾空格
char c[] = s.trim().toCharArray();
//消除多余空格
boolean isSpace = true; //标记前一个字符是否为空格字符
int cur = 0;
for(int i = 0; i < c.length; i ++){
if( c[i] != ' '){//非空格
c[cur ++ ] = c[i];
isSpace = false;
}else if( !isSpace){//前一个字符非空格 且当前字符为空格字符
c[cur ++] = ' ';
isSpace = true;
}
}
//字符串实际有效长度
int len = cur;
//翻转整个字符串
reverse(c, 0, len);
//翻转字符串的单词
int preSpaceIdx = -1;
for(int i = 0; i < len; i ++){
if( c[i] != ' ') continue;
//i为空格位置
reverse(c, preSpaceIdx + 1, i);
preSpaceIdx = i;
}
reverse( c, preSpaceIdx + 1, len);
return new String(c, 0, len);
}
}
总结
字符串类算法题,在测试时尽可能考虑空字符串和null的边界情况。
以上是关于面试算法题 专题 —— 字符串的主要内容,如果未能解决你的问题,请参考以下文章