zoukankan      html  css  js  c++  java
  • 字符串包含

    问题描述:给定两个字符串str1和str2,字符串中只包含字母(区分大小写)和数字,并且字符可以重复。现在让你判断str2中的字符是否都来源于str1中的字符(假设str1的长度为m,str2的长度为n)?限制时间复杂度为O(m+n),空间复杂度为O(1)。

    分析:采用暴力法可以很容易地实现要求,但是效率比较低,需要拿str2中的每一位去和str1中的每一位进行比较,因此时间复杂度为O(mn),不符合题目要求,空间复杂度为O(1)。

            因此需要寻找新的解法。这里我提供三种解法,第一种解法利用哈希表,第二中解法利用素数,第三种解法利用编码(在穷举子集合问题中我已经详细介绍过了,读者可以查看我的博客中的“穷举子集合”这篇博客,这里不再解释)。

           第一种解法(利用哈希表):

                            可以建立一个长度为62的哈希表,用布尔数组模拟即可,因为布尔类型占用字节少,可以节省空间。

                            假设这个数组为hash[0]~hash[61],一开始全部初始化为false,表示不存在这个字符,其中下标0~25对用A~Z,26~51对应a~z,52~61对应0~9。

                            将str1中的每个字符哈希到对应的数组元素中,并将元素赋值为true。

                            可以先假设str2中的字符全部来自于str1中,可以设置标志变量实现。

                            将str2中的每个字符也哈希到对应的位置,如果该位置元素为true,那么接着往后比较,如果比较到某一位,哈希表中某元素为false,那么直接返回false即可。

                            如果执行到最后都没有返回false,那么说明str2中的字符全部来自于str1中,返回true即可。

                            这种解法就是利用哈希表实现,需要浪费一点空间,但是可以满足题目要求,并且时间复杂度为O(m+n),空间复杂度为O(1)。

            第二种解法(利用素数):

                           我们可以让字符串str1中的每个字符与一个素数对应,从2开始,往后类推,A对应2,B对应3,C对应5,......。遍历第一个字符串str1,把每个字符对应素数相乘。最终会得到一个整数。

                           利用上面字母和素数的对应关系,对应第二个字符串中的字符,然后轮询,用每个字符对应的素数除前面得到的整数。

                           如果结果有余数,说明该字符不是来源于str1中,直接返回false。

                           如果整个过程中没有余数,则说明第二个字符串是第一个的子集了(判断是不是真子集,可以比较两个字符串对应的素数乘积,若相等则不是真子集)。

                           算法的时间复杂度为O(m+n),最好的情况为O(n)(遍历短的字符串的第一个数,与长字符串素数的乘积相除,即出现余数,便可退出程序,返回false),空间复杂度为O(1),符合题目要求。

                           但是该方法存在一个致命的问题,就是数据可能会溢出,因为当字符串很长时,所得的成积会很大,极有可能会溢出,所以这种方法慎用。

              第三种解法(利用编码):

                          可以将所有可能出现的字符进行编码,一共可能出现62个字符,可以用62位对其进行编码,1表示对应的位出现,0表示对应的位不出现。

                          62位的编码,可以使用8个字节实现,大多数编程语言中都有长整型类型,所定义的是占用8个字节,我们可以使用它的低62位。

                          假设A~Z对应低0~25位, a~z对应低26~51位, 0~9对应低52~61位。

                          可以扫描str1中的每个字符,进行置位。

                          可以先假设str2中的字符全部来自于str1中,再去找假设不成立的条件。

                          再扫描str2中的每个字符,判断标志位是否为0,若为0,说明该字符在str1中不存在,直接返回false。直到最后,如果还没有返回false,那么就返回true。

                          算法的时间复杂度为O(m+n),空间复杂度为O(1),符合题目要求。

              三种解法比较:

                          解法1占用空间大,但是计算比较简单,编程也简单。

                          解法2占用空间小,但是计算比较复杂,需要去寻找素数,编程复杂,最大的问题是可能会溢出。

                          解法3占用空间小,但是计算比较复杂,用到了位运算,编程相对简单。需要去找一个满足编码位数的类型变量。

                          综上所述:三种方法各有利弊,读者可以根据自己的应用场合以及自己的编程习惯选用适当的方法,当然还可能存在其他的解法。

              注意无论是哪种解法,我们要学会每种解法的思想,而不是只会应用到这个题目中,比如在我之前的博客中,有一个问题是穷举子集合,当时我就用到了字符编码,这里我同样用到了字符编码。

              总之学算法,一定要学算法思想,而不是某一道题。

              鉴于哈希表和素数这两种解法,大家都曾经用过,或者写过类似的程序,这里就不再写详细的代码了。

              由于位运算大家用的不多,这里我写出了解法3详细的Java代码实现,都是通用的算法语句,读者可以很容易转换为其他语言。具体的Java代码如下:

     1 import java.util.*;
     2  class Test {
     3       public static boolean StringContain(String str1,String str2){
     4           long code=0;                                            //用来编码的变量,8个字节,共64位,我们只用低62位
     5           for(int i=0;i<str1.length();i++)                        //扫描str1字符串,为对应的字符置位
     6           {
     7               if(str1.charAt(i)>='A' && str1.charAt(i)<='Z')      //A~Z
     8                    code |= (1 << (str1.charAt(i) - 'A'));
     9               if(str1.charAt(i)>='a' && str1.charAt(i)<='z')      //a~z
    10                   code |= (1 << ((str1.charAt(i) - 'a')+26));
    11               if(str1.charAt(i)>='0' && str1.charAt(i)<='9')      //0~9
    12                   code |= (1 << ((str1.charAt(i) - '0')+52));
    13           }
    14           
    15           for(int i=0;i<str2.length();i++)                         //扫描str2字符串,对应的字符进行判断
    16           {
    17               if(str2.charAt(i)>='A' && str2.charAt(i)<='Z')
    18                   if ((code & (1 << (str2.charAt(i) - 'A'))) == 0)
    19                       return false;
    20               if(str2.charAt(i)>='a' && str2.charAt(i)<='z')
    21                   if ((code & (1 << ((str2.charAt(i) - 'a')+26))) == 0)
    22                       return false;
    23               if(str2.charAt(i)>='0' && str2.charAt(i)<='9')
    24                   if ((code & (1 << ((str2.charAt(i) - '0')+52))) == 0)
    25                       return false;
    26           }
    27           
    28           return true;
    29       }
    30     }
    31  
    32 public class Main {
    33     public static void main(String[] args) {
    34       String str1=new String("a3ABCD");
    35       String str2=new String("BADa3");
    36       System.out.println("str1="+str1);
    37       System.out.println("str2="+str2);
    38       System.out.println("是否包含(true/false):"+Test.StringContain(str1,str2));
    39       
    40     }
    41 
    42 }
    View Code

    输出结果为:

    str1=a3ABCD
    str2=BADa3
    是否包含(true/false):true

              

                                

  • 相关阅读:
    c++引用(reference)
    c++ 三目运算符功能增强
    C++ “新增”bool类型关键字
    C++ struct
    C++命名空间(namespace)
    基于python 实现KNN 算法
    Chrome 快捷键使用
    WOE(weight of evidence, 证据权重)
    python 命令运行环境下 ModuleNotFoundError: No module named 'Test'
    基于python 信用卡评分系统 的数据分析
  • 原文地址:https://www.cnblogs.com/guozhenqiang/p/5427088.html
Copyright © 2011-2022 走看看