zoukankan      html  css  js  c++  java
  • 【LeetCode算法】LeetCode初级算法——字符串

      在LeetCode初级算法的字符串专题中,共给出了九道题目,分别为:反转字符串,整数反转,字符串中的第一个唯一字符,有效的字母异位词,验证回文字符串,字符串转换整数,实现strStr(),报数,最长公共前缀。涉及到字符串的常规应用和一些算法技巧,依次记录如下。


    反转字符串(344)

      题目描述:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
      例如:输入:["h","e","l","l","o"]      输出:["o","l","l","e","h"]
      解析:
      若给出的是字符数组,则可以直接通过简单的首尾交换完成反转;若给出的是一个字符串,有三种方法:(1)将String变为StringBuffer,借助StringBuffer类的reverse()方法直接完成反转;(2)通过String的toCharArray()方法变为字符数组,首尾交换;(3)借助栈的先进后出实现。

        //方法一:首尾交换,输入为字符数组
        public void reverseString(char[] s) {
            int len = s.length;  //数组长度
            for(int i=0;i<len/2;i++){  //遍历一半长度,首尾交换
                char temp=s[i];
                s[i]=s[len-i-1];
                s[len-i-1]=temp;
            }
        }
        //方法二:StringBuffer的reverse方法,输入为String
        public String reverseString(String s) {
            StringBuffer result = new StringBuffer(s);
            result = result.reverse();  //调用reverse方法
            return result.toString();
        }
    

    整数反转(7)

      题目描述:给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转,反转后如果整数溢出则返回0。
      例如: 输入: 123 输出: 321   输入: -123 输出: -321
      解析:
      类似于反转字符串,整数反转也有两种方法,(1)将整数转为字符串,通过字符串的反转来实现整数的反转,整数转字符串String.valueOf(num),字符串转整数Integer.parseInt(str);(2)弹出和压入数字,通过pop = x % 10;取出末尾数字,压入到结果的前面。显然,方法二较好。
      这里需要注意的还有整数溢出的处理,同样有两种方法,一种可以通过java的异常机制,捕获对应的整数溢出异常,进行处理(NumberFormatException),还有一种是进行提前进行数字是否溢出的检查。

        //方法一,转为字符串实现
        public int reverse(int x) {
            String s=String.valueOf(x);
            if(x<0)
                s=s.substring(1);
            StringBuffer strBuf = new StringBuffer(s);
            String str=strBuf.reverse().toString();
            try{
                return x>0 ? Integer.parseInt(str) : -1*Integer.parseInt(str);
            }catch(NumberFormatException e){
                return 0;
            }
        }
        
        //方法二,弹出和推入数字 & 溢出前进行检查
        public int reverse(int x) {
            int rev = 0;
            while (x != 0) {
                int pop = x % 10;  //弹出末尾数字
                x /= 10;
                if (rev > Integer.MAX_VALUE/10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) return 0;
                if (rev < Integer.MIN_VALUE/10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) return 0;
                rev = rev * 10 + pop;  //推入
            }
            return rev;
        }
    

    字符串中的第一个唯一字符(387)

      题目描述:给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
      例如:s = "leetcode" 返回 0.    s = "loveleetcode",返回 2.
      解析:
      方法一:用哈希表建立每个字符和其出现次数的映射,然后按顺序遍历字符串,找到第一个出现次数为1的字符,返回其位置即可。方法二:使用模式匹配从前(indexOf)和从后(lastIndexOf)匹配每一个字符,相等即为唯一。

        //方法一:哈希表
        public int firstUniqChar(String s) {
            Map<Character,Integer> map = new HashMap<Character,Integer>();
            for(int i=0;i<s.length();i++){
                char c=s.charAt(i);
                map.put(c,map.getOrDefault(c,0)+1);
            }
            for(int i=0;i<s.length();i++)
                if(map.get(s.charAt(i))==1)
                     return i;
            return -1;
        }
        
        //方法二:模式匹配
        public int firstUniqChar(String s) {
            for(int i=0;i<s.length();i++){
                char c=s.charAt(i);
                if(s.indexOf(c)==s.lastIndexOf(c))
                    return i;
            }
            return -1;
        }
    

    有效的字母异位词(242)

      题目描述:给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。
      例如:输入: s = "anagram", t = "nagaram"   输出: true
      解析:
      方法一:用哈希表建立每个字符和其出现次数的映射,然后判断两个字符串建立的哈希表是否相同;方法二,将字符串转为字符数组,对字符数组排序,比较排序之后的数组是否相同。方法三:哈希表的变化,如果只有小写字母,可以用数组代替哈希表,一个字符串+,一个字符串-,若是字母异位词,所有值都为0.

        //方法一:哈希表
        public boolean isAnagram(String s, String t) {
            if(s.length()!=t.length())
                return false;
            Map<Character,Integer> s_map=new HashMap<Character,Integer>();
            Map<Character,Integer> t_map=new HashMap<Character,Integer>();
            for(int i=0;i<s.length();i++){
                char si=s.charAt(i),ti=t.charAt(i);
                s_map.put(si,s_map.getOrDefault(si,0)+1);
                t_map.put(ti,t_map.getOrDefault(ti,0)+1);
            }
            if(s_map.entrySet().equals(t_map.entrySet()))  
                return true;
            return false;
        }
        //方法二:字符数组排序
        public boolean isAnagram(String s, String t) {
        	if (s.length() != t.length()) {
           		return false;
        	}
        	char[] str1 = s.toCharArray();
        	char[] str2 = t.toCharArray();
        	Arrays.sort(str1);
        	Arrays.sort(str2);
        	return Arrays.equals(str1, str2);
    	}
    	//方法三:哈希表的变化,如果只有小写字母,可以用数组代替哈希表
        public boolean isAnagram(String s, String t) {
        	if (s.length() != t.length())
            	return false;
        	int[] counter = new int[26];
        	for (int i = 0; i < s.length(); i++) {
            	counter[s.charAt(i) - 'a']++;
            	counter[t.charAt(i) - 'a']--;
        	}
        	for (int count : counter) {
            	if (count != 0)
                	return false;
        	}
       	 	return true;
    	}
    

    实现strStr() (28)

      题目描述:给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回-1。
      例如:输入: haystack = "hello", needle = "ll"    输出: 2
      本题与C语言的 strstr() 以及 Java的 indexOf() 定义相符。具体可以参见另一篇博客"串的模式匹配算法"。

    验证回文串 (125)

      题目描述:给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。将空字符串定义为有效的回文串。
      例如:输入: "A man, a plan, a canal: Panama"    输出: true
      解析:
      方法一:暴力解决,去掉字符串中的特殊字符,将所有字母变为小写(toLowerCase),然后首尾比较。方法二:对撞指针(类似于快排的思想),两端扫描,特殊字符跳过

        //方法一:暴力解决
        public boolean isPalindrome(String s) {
            s=s.toLowerCase(); //全部变为小写
            char[] strArray = s.toCharArray(); //变为字符数组
            String str = "";
            for(char c:strArray)
                if((c>='a'&&c<='z')||(c>='0'&&c<='9'))
                    str += c;
            int len=str.length();
            for(int i=0;i<len/2;i++)
                if(str.charAt(i)!=str.charAt(len-1-i))
                    return false;
            return true;
        }
        //方法二:对撞指针
         public boolean isPalindrome(String s) {
            s=s.toLowerCase();  //全部变为小写
            int len=s.length();
            for(int i=0,j=len-1;i<j;){
                char si=s.charAt(i),sj=s.charAt(j);
                if(!((si>='a'&&si<='z')||(si>='0'&&si<='9'))) 
                    i++;
                else if(!((sj>='a'&&sj<='z')||(sj>='0'&&sj<='9'))) 
                    j--;
                else if(si!=sj)
                    return false;
                else{
                    i++;
                    j--;
                }
            }
            return true;
        }
    

    字符串转换整数atoi(8)

      题目描述:请你来实现一个 atoi 函数,使其能将字符串转换成整数。首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
      例如:输入: "4193 with words" 输出: 4193
      解析:题目比较简单,依次处理字符串开头为数字,为"+",为"-",或者为其他字符的情况。

        public int myAtoi(String str) {
            str = str.trim(); //去掉两边的空格
            if(str.length()==0)
                return 0;
            char first = str.charAt(0);
            String res="";
            if(first>='0'&&first<='9')  //首字母为数字
                res += first;
            else if(first=='-'||first=='+'){  //首字母为正负号,需要判断后续字符是否为数字
                if(str.length()==1)
                    return 0;
                char second = str.charAt(1);
                if(second>='0'&&second<='9')
                    res +=first;
            }
            if(res!=""){ //存在数字
               for(int i=1;i<str.length();i++){
                    char c=str.charAt(i);
                    if(c>='0'&&c<='9')
                        res += c;
                    else
                        break;
                }
            }
            else return 0;
            
            try{
                return Integer.parseInt(res);
            }catch(NumberFormatException e){
                if(res.charAt(0)=='-')
                    return Integer.MIN_VALUE;
                else
                    return Integer.MAX_VALUE;
            }
        }
    

    报数(38)

      报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下,给定一个正整数 n(1 ≤ n ≤ 30),输出报数序列的第 n 项。
      1. 1
      2. 11
      3. 21
      4. 1211
      5. 111221
      解析:当前n的报数结果是基于n-1的报数结果,基于n-1的结果对字符串进行分析即可,相当于按序统计n-1结果的字符种类及数目。

        public String countAndSay(int n) {
            String[] res = new String[n+1];
            res[1]="1";
            for(int i=2;i<n+1;i++){
                int count=1;
                res[i]="";
                char pre = res[i-1].charAt(0); //前一个字符
                for(int j=1;j<res[i-1].length();j++){
                    char c=res[i-1].charAt(j);
                    if(c==pre) //相等计数加一
                        count++;
                    else{      //不相等写入结果,count重新置为1
                        res[i] += String.valueOf(count)+pre;
                        count = 1;
                        pre = c;
                    }
                }
                res[i] += String.valueOf(count)+pre;
            }
            System.out.println(res[n]);
            return res[n];
        }
    

    最长公共前缀 (14)

      编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。所有输入只包含小写字母 a-z 。
      例如:输入: ["flower","flow","flight"] 输出: "fl"
      解析:此题有多种方法可解,方法一:水平扫描法,前i个字符串的公共前缀与第i+1个字符串的公共前缀就是前i+1个字符串的公共前缀;方法二:垂直扫描法,将每个字符串的第i个字符(i从0开始)依次比较;方法三:分治法,用分治的思想将原问题分解为两个子问题。

        //方法一:水平扫描
        public String longestCommonPrefix(String[] strs) {
            if(strs.length==0)
                return "";
            //最长公共前缀长度不会超过其中的任意一个
            String prefix = strs[0];  //假设第一个字符串就是最长公共前缀,通过比较逐渐缩短
            int j;
            for(int i=1;i<strs.length;i++){
                int len=strs[i].length()<prefix.length() ? strs[i].length() : prefix.length();
                for(j=0;j<len;j++){ //与strs[i]比较,最长可能为len
                    if(prefix.charAt(j)!=strs[i].charAt(j))
                        break;  //不等,停止,最长为j
                }
                if(j==0)
                    return "";
                prefix = prefix.substring(0,j);
                
            }
            return prefix;
        }
        //方法三:分治法
        public String longestCommonPrefix(String[] strs) {
        	if (strs == null || strs.length == 0) return "";    
            return longestCommonPrefix(strs, 0 , strs.length - 1);
    	}
    
    	private String longestCommonPrefix(String[] strs, int l, int r) {
        	if (l == r)
            	return strs[l];
        	else {
            	int mid = (l + r)/2;
            	String lcpLeft =   longestCommonPrefix(strs, l , mid);
            	String lcpRight =  longestCommonPrefix(strs, mid + 1,r);
            	return commonPrefix(lcpLeft, lcpRight);
       		}
    	}
    
    	String commonPrefix(String left,String right) {
        	int min = Math.min(left.length(), right.length());
        	for (int i = 0; i < min; i++)
            	if ( left.charAt(i) != right.charAt(i) )
                	return left.substring(0, i);
        	return left.substring(0, min);
    	}
    

    总结

      本博客记录了LeetCode初级算法中的字符串模块给出的9道典型题目,重点是字符串的相关操作以及主要的算法技巧的应用,如对撞指针,哈希表,分治思想等等。

    博学 审问 慎思 明辨 笃行
  • 相关阅读:
    streamsets 集成 cratedb 测试
    streamsets k8s 部署试用
    streamsets rest api 转换 graphql
    StreamSets sdc rpc 测试
    StreamSets 相关文章
    StreamSets 多线程 Pipelines
    StreamSets SDC RPC Pipelines说明
    StreamSets 管理 SDC Edge上的pipeline
    StreamSets 部署 Pipelines 到 SDC Edge
    StreamSets 设计Edge pipeline
  • 原文地址:https://www.cnblogs.com/gzshan/p/10709338.html
Copyright © 2011-2022 走看看