常用算法思路分析系列字符串高频题集

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是否互为旋转词。

    举例:

以上是关于常用算法思路分析系列字符串高频题集的主要内容,如果未能解决你的问题,请参考以下文章

常用算法思路分析系列链表相关高频题集

搞定kmp算法

搞定kmp算法

KMP算法实践与简单分析

字符串kmp算法详解

字符串匹配算法系列一:KMP算法原理