第五章 字符串专题(上)

1、判断字符串有无重复字符  

class test{
    public static void main(String[] args) {
        String inistring = "abcdaef";
        System.out.println(check(inistring));
    }
    static boolean check(String inistring){
        int [] flag = new int[128];   //128ascall码的用法
        for(int i=0;i<inistring.length();i++){
            int c = (int)(inistring.charAt(i));
            if(flag[c]>0) return false;
            else flag[c]++;
        }
        return true;
    }
}

2、巧妙翻转字符串

例如 : this is nowcoder

翻转为: redcowon si siht

class test{
    public static void main(String[] args) {
        String inistring = "abcdaef";
        
    }
    static String  reverstring(String inistring){
        int len = inistring.length();
        char[] chararr = new char[len];
        for(int i=0;i<len;i++){
            chararr[i]  = inistring.charAt(len-i+1);
            
        }
        return new String(chararr);
    }
}
class test{
    public static void main(String[] args) {
        String inistring = "abcdaef";

    }
    static String  reverstring(String inistring){
        StringBuffer sb = new StringBuffer(inistring);
        return sb.reverse().toString();
    }
}

3、变形词问题

两个字符串,,其中一个能不能通过换一换顺序变成另一个字符串

思路:把两个换成字符数组,,然后arrays.sort排序,,最后Arrays.equals(a,b)就可以了

4、替换字符串中的空格(空格换成%20)

class test{
    public static void main(String[] args) {
        String inistring = "abcdaef";
        System.out.println(replace("haha hehe"));
    }
    static String replace(String inistring){
        return inistring.replaceAll("\\s","%20");
    }
}

但是这个题主要不是考察这个,,,而是 字符串不可变,,增加空间的问题

class test{
    public static void main(String[] args) {
        String inistring = "abcdaef";
        System.out.println(replace("haha hehe0000000000000000".toCharArray(),15));
    }
    static String replace(char[] inistring,int length){
        int count = length;
        for(int i=0;i<length;i++){
            if(inistring[i] == ' '){
                count+=2;
            }
        }
        //然后用快慢指针做
        int p1 = length-1;
        int p2 = count-1;
        while (p1>=0){
            if(inistring[p1]==' '){
                inistring[p2--] = ' ';
                inistring[p2--] = '2';
                inistring[p2--] = '%';

            }else {
                inistring[p2--] = inistring[p1];
            }
            p1--;
        }
        return new String(inistring,0,count);
    }
}

5、压缩字符串(字符串统计)

aabbccccaaa

扫描二维码关注公众号,回复: 5476135 查看本文章
class test2{
    public static void main(String[] args) {
        
    }
    static String zipString(String str){
        int count =0;
        char last =0;
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<str.length();i++){
            char charat = str.charAt(i);
            if (sb.length() == 0) {
                sb.append(charat);
                count++; //加第一个字符
            }else {
                if(last==charat){
                    count++;
                }else {
                    sb.append(count).append(charat);
                    count++;
                    
                }
            }
            last = charat;
        }
        if(count>=1){
            sb.append(count);
        }
        if(sb.length()>=str.length()){
            return str;
        }
        return sb.toString();
    }
    
    
}

6、问两个字符串的字符集相同否?

class test2{
    public static void main(String[] args) {

    }
    static boolean check(String str1,String str2){
        int []help = new int[256];
        for(int i=0;i< str1.length();i++){
            char c = str1.charAt(i);
            if (help[c] == 0) {
                
                help[c] =1;
            }
        }
        for(int i=0;i<str2.length();i++){
            char c = str2.charAt(i);
            if(help[c]==0){
                return false;
            }
        }
        return true;
    }
}

用hashmap的话就不用限制256 还是128,,,这样就不需要要求hashmap了

import java.util.HashMap;
import java.util.Map;

class test2{
    public static void main(String[] args) {

    }
    static boolean check(String str1,String str2){
        Map<Character,Integer> map = new HashMap<>();
        for(int i=0;i< str1.length();i++){
            char c = str1.charAt(i);
            if (map.get(c)==0) {

                map.put(c,1);
            }
        }
        for(int i=0;i<str2.length();i++){
            char c = str2.charAt(i);
            if(map.get(c)==null){
                return false;
            }
        }
        return true;
    }
}

7、旋转词

给两个串,s1和s2,问s2能否通过s1做循环移位得到

s1:A A B C D

s2:C D A A 

思路:s1×2  看看包不包含s2就可以了

class test2{
    public static void main(String[] args) {

    }
    static boolean check(String str1,String str2){
        StringBuilder sb = new StringBuilder();
        sb.append(str1).append(str1);
        return sb.toString().contains(str2);
    }
}

8、单词的翻转(其实stringbuilder 和stringbuffer的区别不太大,但是推荐用stringbuild 因为他的速度会快一些)

here you are

转成 are you here

class test2{
    public static void main(String[] args) {
        String res = reverse("here you are");

    }
    static String reverse(String str){
        String s1 = reversestring(str);

        String []words = s1.split("\\s");
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<words.length;i++){
            sb.append(reversestring(words[i]+" "));
        }
    }
    static String reversestring(String str){
        StringBuilder sb = new StringBuilder(str);
        return sb.reverse().toString();
    }

}

9、去掉字符串中连续出现k次的0  

java正则操作

class test2{
    public static void main(String[] args) {
       String res = remove("here you000are",3);
        System.out.println(res);
    }
    static String remove(String str,int k){
        String regexp = "0{"+k+"}";
        return str.replaceAll(regexp,""); //前面直接写的话加双引号"0{3}"
    }
}

10、最短摘要

集齐所有关键字的最短串

例题:一个产品的英文简介,包含m个英文单词,每个单词空格分开,没有其他标点符号,给n个单词关键字,

思路:借鉴尺取法

思路:如果暴力的话每一次都要从下一个重新来,,尺取法比较跳跃,i和j两个指针,,i先找到第一个n,然后j从i开始往后找,知道包含所有的关键字停止,然后i再向后扫,找到下一个关键字停止,,看从i到j是否包含所有的关键字,若包含,更新最短距离,若不包含,,j往下扫,直到包含完所有的关键字停止,计算最短路。。更新,,,,过程中,i和j从不回头

class test2{
    public static void main(String[] args) {
        solve2(new String[]{"a", "b", "c", "seed", "c", "e", "f", "c", "c", "seed", "e", "f", "seed", "c"}, new String[]{"c", "c", "e", "f"});
    }
    static void solve2(String []w, String [] keys){

        int begin = -1; //begin和end是确定的头和尾
        int end = -1;//而j和i是下一次移动的光标
        int j=-1;
        int minlen = 0;
        int p2 = -1;
        int len =0;
        int []keyfound = new int[keys.length];//判断重复关键字 不让一个关键字出现好几次而别的没有出现
        for(int i=0;i<w.length;i++){
            String w1 = w[i];
            int index = indexOf(keys,w1);
            if(index == -1){
                continue; //没找着,,意思就是他不是关键字
            }
            else { //i是一个关键字,
                keyfound[index] = 1;
            }
            if(p2!=j){
                j = p2;
            }else {
                j=i+1;
            }
            for (;j<w.length;j++){
                String w2 = w[j];
                int index1 =indexOf(keys,w2);
                if(index1==-1 || keyfound[index1]==1){
                    continue;
                }else {  //扎到了
                    keyfound[index1] = 1;
                    if(sum(keyfound) == keys.length){
                        p2 = j;
                        if(j-i+1<len){
                            len = j=i+1;
                            begin = i;
                            end = j;
                        }
                    }
                }
            }
        }

    }
    static int sum(int [] keyfound){
        int sum =0;
        for (int e:keyfound){
            sum+=e;
        }
        return sum;
    }
    static int indexOf(String []keys,String w){
        for(int i=0;i<keys.length;i++){
            if(keys[i].equals(w)){
                return i;
            }
        }
        return -1;
    }


}

上面代码可能不对,,下面是正确的代码 


import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class Edge {
    public static void main(String[] args) {
        solve2(new String[]{"a", "b", "c", "seed", "c", "e", "f", "c", "c", "seed", "e", "f", "seed", "c"}, new String[]{"c", "c", "e", "f"});
        solve2(new String[]{"a", "b", "a", "a", "b", "c", "d", "h", "e", "f", "f"}, new String[]{"b", "c", "d"});
    }

    public static void solve2(String[] w, String[] keys) {
        Arrays.sort(keys);
        //begin和end用于在找到更短的包含全部关键字的子数组时更新
        int begin = -1;
        int end = -1;

        int j = -1;//上一次囊括了所有关键字的右边界

        int minLen = Integer.MAX_VALUE;
        for (int i = 0; i < w.length; i++) {
            //如果i位置是关键字,求以i开头包含所有关键字的序列
            String word1 = w[i];
            int index = Arrays.binarySearch(keys, word1);
            if (-1 == index) {
                continue;
            }
            if (j == -1){
                j = i + 1;
            }
            while (j < w.length) {
                String word2 = w[j];//文章单词
                int index1 = Arrays.binarySearch(keys, word2);
                if (index1 == -1) {
                    j++;
                    continue;
                } else {//找到关键字
                    if (containsAll(keys, w, i, j)) {//全部到齐
                        if (j - i + 1 < minLen) {//更新
                            minLen = j - i + 1;
                            begin = i;
                            end = j;
                        }
                        break;
                    } else {
                        j++;
                    }
                }
            }
        }
        print(w, begin, end);
    }

    private static void print(String[] w, int begin, int end) {
        System.out.println(begin + " " + end);
        for (int i = begin; i <= end; i++) {
            System.out.print(w[i] + " ");
        }
        System.out.println();
    }

    private static boolean containsAll(String[] keyWords, String[] w, int i, int j) {
        Map<String, Integer> map = new HashMap<>();
        for (int k = 0; k < keyWords.length; k++) {
            String key = keyWords[k];
            if (map.get(key) == null) {
                map.put(key, 1);
            } else {
                map.put(key, map.get(key) + 1);
            }
        }
        Map<String, Integer> map2 = new HashMap<>();

        for (int k = i; k <= j; k++) {
            String key = w[k];
            if (map2.get(key) == null) {
                map2.put(key, 1);
            } else {
                map2.put(key, map2.get(key) + 1);
            }
        }
        for (Map.Entry<String, Integer> e :
                map.entrySet()) {
            if (map2.get(e.getKey()) == null || map2.get(e.getKey()) < e.getValue()) {
                return false;
            }
        }
        return true;
    }
}

11、开启字符串匹配的算法  

第一种:RabinKarp

第二种:KMP

第三种:前缀树(字典树 ,Trie);后缀数组

第一种:(滚动哈希)

ABABAB

BAB

下面三个求哈希,上面从每一个开始,每三个求一个哈希,,,比对哈希值

小tip:C0*31^2 + C1*31^1+C2

可以用递推循环施展开((0+C0)×31+C1)×31+C2

普通哈希:

class test{
    public static void main(String[] args) {

    }
    static long hash(String str){
        long hash = 0;
        for (int i=0;i!=str.length();i++){
            hash += 31*hash+str.charAt(i);
        }
        return hash;
    }
    static void match(String p,String s){
        long hash_p = hash(p);
        for (int i=0;i+p.length()<=s.length();i++){
            long hash = hash(s.substring(i,i+p.length()));
            if(hash == hash_p){
                System.out.println("match");
            }
        }
    } 
}

滚动哈希:不用每三个都算一次了,可以递推求出后面的哈希

哈希冲突:十万数据,波动大概在0~3;百万数据,冲突大概在110+ ,竞赛中不太需要去补那一下,,如果想的话再判断一次 模式串是否等于字符串就可以了

class test{
    public static void main(String[] args) {

    }
    static long[] hash(String s,int n){ //n代表几个求一个hash
        long[] res = new long[s.length()-n+1];
        res[0] = hash(s.substring(0,n));
        for(int i=n;i<s.length();i++){
            char newchar = s.charAt(i);
            char ochar = s.charAt(i-n);
            long v =(res[i-n]*seed +newchar-NExponent.ex2(seed,n)*ochar)%long.MAX_VALUE;
            res[i-n+1] = v;
        }
        return res;
    }

}

第二种:KMP(next数组很重要)kmp整体的复杂度在O(m+n)

这里的next数组好像不太对,因为next数组第一个数字是-1,而第二个数字是0 这里的next数组求出来是0 0 1 2 3 但是整体是对的,,不懂的话可以把案例带进去感受一下,,,很好懂的

public class test6 {
    public static int kmp(String str, String dest,int[] next){//str文本串  dest 模式串
        for(int i = 0, j = 0; i < str.length(); i++){
            while(j > 0 && str.charAt(i) != dest.charAt(j)){
                j = next[j - 1];
            }
            if(str.charAt(i) == dest.charAt(j)){
                j++;
            }
            if(j == dest.length()){
                return i-j+1;
            }
        }
        return 0;
    }
    public static int[] kmpnext(String dest){
        int[] next = new int[dest.length()];
        next[0] = 0;
        for(int i = 1,j = 0; i < dest.length(); i++){
            while(j > 0 && dest.charAt(j) != dest.charAt(i)){
                j = next[j - 1];
            }
            if(dest.charAt(i) == dest.charAt(j)){
                j++;
            }
            next[i] = j;
        }
        return next;
    }
    public static void main(String[] args){
        String a = "ababa";
        String b = "ssdfgasdbababa";
        int[] next = kmpnext(a);
        int res = kmp(b, a,next);
        System.out.println(res);
        for(int i = 0; i < next.length; i++){
            System.out.println(next[i]);
        }
        System.out.println(next.length);
    }
}

注释掉的代码是可以变种为计算出现这个字符串的次数的;

kmp的正确姿势

判断模式串在字符串中在哪里出现

class test3{
    public static void main(String[] args) {
        String src = "babababcbabababb";
        int index = indexOf1(src, "bababb");
        System.out.println(index);
    }

    static int indexOf1(String s,String p){
        if(s.length()==0|| p.length()==0) return -1;
        if(p.length()>s.length()) return -1;
    //    int count = 0;
        int next[] = next(p);

        int i = 0;
        int j =0;
        int slen = s.length();
        int plen = p.length();

        while (i<slen){
            if(j==-1 || s.charAt(i) == p.charAt(j)){
                i++;
                j++;
            }else {
                j = next[j];
            }
            if(j==plen){
                return i-j;
            }
        }
        return -1;
    }

    static int[] next(String p){
        int length = p.length();
        int next[]  = new int[length];
        char p_c[]  =p.toCharArray();
        next[0] = -1;
        if(length==1){
            return next;//在这里判断不要越界了,如果长度为一,写next(1)的话就会越界
        }
        next[1] = 0;
        int j = 1;//next(1)= 0   next(j) = k;
        int k = 0;

        //一直到next数组填到完就可以了
        while (j<length-1){
            if(k<0 || p_c[j]==p_c[k]){
                next[j+1] = k+1;
                j = j+1;
                k = k+1;
                //上面这三句可以归为一句就是next[++j] = ++k;  这一句变成next[J+1]=K+1 后还有两句是因为这句过后i和j并没有自增,没有变化
            }else {
                k = next[k];//不同 
            }
        }
        return next;
    }
}

 还有可能字符串中并不只有一个模式串’,需要算有多少个,,代码只需要改几行 标星号的地方就是需要修改的地方

class test3{
    public static void main(String[] args) {
        String src = "babababcbabababb";
        int index = indexOf1(src, "bab");
        System.out.println(index);
    }

    static int indexOf1(String s,String p){
        if(s.length()==0|| p.length()==0) return -1;
        if(p.length()>s.length()) return -1;
        int count = 0;//**********************************
        int next[] = next(p);

        int i = 0;
        int j =0;
        int slen = s.length();
        int plen = p.length();

        while (i<slen){
            if(j==-1 || s.charAt(i) == p.charAt(j)){
                i++;
                j++;
            }else {
                j = next[j];
            }
            if(j==plen){
                count++;//********************************************
                i--;//************************************
                j =next[j-1];//********************************
            }
        }
        return count;//**********************************
    }

    static int[] next(String p){
        int length = p.length();
        int next[]  = new int[length];
        char p_c[]  =p.toCharArray();
        next[0] = -1;
        if(length==1){
            return next;//在这里判断不要越界了,如果长度为一,写next(1)的话就会越界
        }
        next[1] = 0;
        int j = 1;//next(1)= 0   next(j) = k;
        int k = 0;

        //一直到next数组填到完就可以了
        while (j<length-1){
            if(k<0 || p_c[j]==p_c[k]){
               next[++j] = ++k;
            }else {
                k = next[k];//不同 
            }
        }
        return next;
    }
}

最后,,来写一下kmp的暴力法吧

class test3{
    public static void main(String[] args) {
        String src = "babababcbabababb";
        int index = indexOf2(src, "bababb");

        System.out.println(index);
    }
    static int indexOf2(String s,String p){
        int i=0;
        int j=0;
        int sc = 0;
        while (sc<s.length()){
            if(s.charAt(sc)==p.charAt(j)){
                sc++;
                j++;
                if(j==p.length()){
                    return i;
                }
            }else {
                i++;
                sc =i;//扫描指针sc以i为起点
                j=0;// j恢复0
            }
        }
        return -1;
    }
}

第三种:后缀数组:暂且先不写。。。。

猜你喜欢

转载自blog.csdn.net/h_666666/article/details/86668760