常用算法思路分析系列字符串高频题集
Posted 山代王
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常用算法思路分析系列字符串高频题集相关的知识,希望对你有一定的参考价值。
本文是【常用算法思路分析系列】的第二篇,分析字符串相关的高频题目。第一篇是关于排序相关的高频题,还没有看的同学请移步:【常用算法思路分析系列】排序高频题集
1、KMP字符匹配
对于两棵彼此独立的二叉树A和B,请编写一个高效算法,检查A中是否存在一棵子树与B树的拓扑结构完全相同,即给定两棵二叉树的头结点A和B,请返回一个boolean值,代表A中是否存在一棵同构于B的子树。上述其实就是一个字符匹配的问题,我们将A、B两棵二叉树进行遍历,得到一个字符串,就是判断B串是否是A串的子串。而字符匹配常用的算法采用KMP来实现。关于KMP算法分析,我这篇文章中有详细的介绍:我眼中的KMP。
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
public boolean chkIdentical(TreeNode A, TreeNode B) {
String s1 = getSerializString(A);
String s2 = getSerializString(B);
return kmp(s1,s2);
}
//遍历二叉树得到字符串
private static String getSerializString(TreeNode head){
if(head == null)
return null;
StringBuilder sb = new StringBuilder();
sb.append(head.val).append(getSerializString(head.left)).append(getSerializString(head.right));
return sb.toString();
}
public static boolean kmp(String str1,String str2){
char[] strA = str1.toCharArray();
char[] strB = str2.toCharArray();
int[] next = getNextArray(strB);
int i,k;
for(i = 0,k = 0; i < strA.length; i++){
while(k > 0 && strA[i] != strB[k])
k = next[k-1];
if(strA[i] == strB[k]){
k++;
}
/*
* 注意,这里和求next数组有一点区别,因为kmp里面是主串和子串进行比较,当子串最后一个元素都相等的时候,k就相当于是子串和主串相同的公共部分长度,
* 而对于求next数组中的方法来说,相当于是自身和自身进行比较
*/
if(k == strB.length){
return true;
}
}
return false;
}
private static int[] getNextArray(char[] chs){
int i;//字符数组的下表指示器
int k;//前一个字符处的最大公共(相等)前、后缀子串的长度
int[] next = new int[chs.length];
for(i = 1,k = 0; i < chs.length; i++){
while(k > 0 && chs[i] != chs[k])
k = next[k - 1];
if(chs[i] == chs[k]){
k++;
}
next[i] = k;
}
return next;
}
/** 解法二:暴力破解法
* 时间复杂度为O(m*n),m、n分别为strA.length()和strB.length()
* @param strA
* @param strB
* @return
*/
private static boolean compareIdentical(String strA,String strB){
int indexA,indexB;
char[] charA = strA.toCharArray();
char[] charB = strB.toCharArray();
for(int i = 0; i < charA.length; i++){
if(charA[i] == charB[0]){
indexA = i;
for(indexB = 0; indexB < charB.length && indexA < charA.length; indexB++){
if(charA[indexA++] != charB[indexB])
break;
}
if(indexB >= charB.length){//表示已经匹配到
return true;
}
}
}
return false;
}
2、判断是否为变形词
对于两个字符串A和B,如果A和B中出现的字符种类相同且每种字符出现的次数相同,则A和B互为变形词,请设计一个高效算法,检查两给定串是否互为变形词。
以int[]数组作为哈希表,字符的ASCII值作为下标进行映射。先以A字符串的每个字符值作为下标进行映射,令其对应位置的值+1,遍历完后,再使用B字符值进行遍历,先查看对应位置是否为0,如果为0了,表示A中没有该字符(或少于B中的字符数量),如果不为0,则将对应位置的值-1。
/**
* 以int[]数组作为哈希表,字符作为下标进行映射。
* 时间复杂度为O(N),空间复杂度为O(N)
* @param A
* @param lena
* @param B
* @param lenb
* @return
*/
public static boolean chkTransform(String A, int lena, String B, int lenb) {
if(A == null || B == null || lena != lenb){
return false;
}
char[] charA = A.toCharArray();
char[] charB = B.toCharArray();
int[] map = new int[256];
for(int i = 0; i < charA.length; i++){
map[charA[i]]++;
}
for(int i = 0; i < charA.length; i++){
if(map[charB[i]]-- == 0)//这里只能是--在后面
return false;
}
return true;
}
/**
* 暴力破解法
* @param A
* @param lena
* @param B
* @param lenb
* @return
*/
public static boolean chkTransform2(String A, int lena, String B, int lenb) {
if(A == null || B == null || lena != lenb){
return false;
}
Map<Character,Integer> mapA = new HashMap<Character,Integer>();
Map<Character,Integer> mapB = new HashMap<Character,Integer>();
char[] charA = A.toCharArray();
char[] charB = B.toCharArray();
char key;
for(int i = 0; i < charA.length; i++){
key = charA[i];
if(mapA.containsKey(key)){
mapA.put(key, mapA.get(key) + 1);
}else{
mapA.put(key, 1);
}
}
for(int i = 0; i < charB.length; i++){
key = charB[i];
if(mapB.containsKey(key)){
mapB.put(key, mapB.get(key) + 1);
}else{
mapB.put(key, 1);
}
}
if(mapA.size() != mapB.size()){
return false;
}
Set<Character> keys = mapA.keySet();
for(Character ch : keys){
if(mapA.get(ch) != mapB.get(ch)){
return false;
}
}
return true;
}
3、旋转词
如果一个字符串str,把字符串str前面任意的部分挪到后面去形成的字符串叫做str的旋转词。比如str="1234",str的旋转词有"1234","2341","3412","4123"。给定两个字符串a和b,请判断a和b是否互为旋转词。
以上是关于常用算法思路分析系列字符串高频题集的主要内容,如果未能解决你的问题,请参考以下文章