【2017.11.30】3. Longest Substring Without Repeating Characters-最长字串不重复字符

3.1sub- 子

substring()子字符串
//javascript omen使用substring()从字符串提取一些字符
<script type="text/javascript">

    var str="Hello world!"
    document.write(str.substring(37))
    //输出lo w
</script>

3.2 iterate 迭代

iterate 重复;反复申明

3.3 call 调用

call the function 调用这个函数

3.4 enumerate 枚举

枚举(enum)类型是Java 5新增的特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示。

3.4.1 常量的使用
在JDK1.5之前,我们定义常量都是:public static fianl….。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。

package com;
public enum Color {   
     RED, GREEN, BLANK, YELLOW 
}

使用

package com;
public class B {
    public static void main(String[] args) {
        System.out.println( isRed( Color.BLANK ) ) ;  //结果: false
        System.out.println( isRed( Color.RED ) ) ;    //结果: true
    }
    static boolean isRed( Color color ){
        if ( Color.RED.equals( color )) {
            return true ;
        }
        return false ;
    }
}

或者使用switch

package com; 
public class B { 
    public static void main(String[] args) {
        showColor( Color.RED ); 
    }
    static void showColor(Color color){
        switch ( color ) {
        case BLANK:
            System.out.println( color );
            break;
        case RED :
            System.out.println( color );
            break;
        default:
            System.out.println( color );
            break;
        }        
    }
}

3.4.2 自定义函数

package com;
public enum Color {   
     RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
    private String name ;
    private int index ;
    private Color( String name , int index ){
        this.name = name ;
        this.index = index ;
    }   
    //定义 get 和 set 方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getIndex() {
        return index;
    }
    public void setIndex(int index) {
        this.index = index;
    }
}

使用

package com;
public class B {
    public static void main(String[] args) {
        //输出某一枚举的值
        System.out.println( Color.RED.getName() );
        System.out.println( Color.RED.getIndex() );
        //遍历所有的枚举
        for( Color color : Color.values()){
            System.out.println( color + "  name: " + color.getName() + "  index: " + color.getIndex() );
        }
    }
}
//输出
红色
1
RED name: 红色 index: 1
GREEN name: 绿色 index: 2
BLANK name: 白色 index: 3
YELLO name: 黄色 index: 4

3.4.3 总结

可以把 enum 看成是一个普通的 class,它们都可以定义一些属性和方法,不同之处是:enum 不能使用 extends 关键字继承其他类,因为 enum 已经继承了 java.lang.Enum(java是单一继承)。

3.5 为什么 Set s =new HashSet() 而不是 Set s = new Set()

面向对象思想,转型成父类,对使用基类编程。
set是接口,不能实例化,所以不能有set = new set ;
只能实例化接口的实现类,比如说HashSet。

在java语言中,提供多种不同的结构来组织对象,Set(集合)是其中的一种,本身是一个接口,其迭代时的顺序取决于其具体实现。

典型的实现包括:
- HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放;
- LinkedHashSet:以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代;
- TreeSet:提供一个使用树结构存储Set接口的实现,对象以升序顺序存储,访问和遍历的时间很快。

类推:
List list =new ArrayList();
List是接口,用接口去引用实现类,是针对接口编程。可以很容易的改为其他实现类,比如说LinkedList;
ArrayList list = new ArrayList();
也是可以的,但一般不要这样用,除非你要用ArrayList里相对接口没有的方和梳理

3.6 java.lang.Math.max(double a,double b)

可以是double 类型,也可以是int类型,float类型),返回最大值。

//声明
public static double max(double a, double b)

3.7 new HashSet<>() 和 new HashSet< Object >() 是以一样的

里面保存的都是Object类型。

3.8 Character是char的包装类,就像 Integer 和 Int,以及 Long 和 long 一样

Character是char 的包装类,注意它是一个类,提供了很多方法的。

//Set<Character> set = new HashSet<>();
//包装类和基本类型可以自动转换,这是jdk1.5(5.0)的新特性,叫做自动封箱和自动解封
char ch='a';
Character ch1=ch;//自动封箱
Character c=new Character(a);
char c1=c;//自动解封

3.8 s.charAt( i ) 是什么意思

就是字符串 s 在第 i 个位置的字符。

3.9 set.contains(i) 判断Set集合是否包含指定对象

语法 :boolean contains(Object o)

返回值:如果Set集合包含指定的对象,则返回true;否则返回false。

参数:o是要查询的对象。

//使用HashSet类构建Set集合对象,并添加一些内容,然后使用contains方法判断Set集合是否包含指定的对象。

public static void main(String[] args) {

    Set set = new HashSet();
    set.add(new Date());   //向列表中添加数据

    set.add("apple");    //向列表中添加数据

    set.add(new Socket());   //向列表中添加数据

    boolean contains = set.contains("apple");

    if (contains) {

   System.out.println("Set集合包含字符串apple");

    } else {

   System.out.println("Set集合不包含字符串apple");

    }

}

3.10 naive algorithms 朴素的算法

optimize 优化

3.11 A sliding window 滑动窗口

Convolution algorithm using a sliding time-window 时间滑动卷积

这里写图片描述

3.12 mapping 映射

对象关系映射 (Object Relational Mapping ,简称ORM )

语法: map.containsKey(Object key)
该方法判断Map集合对象中是否包含指定的键名。如果map中包含指定的键名,则返回true,否则返回false。

public static void main(String[] args) {
  Map map = new HashMap();       //定义Map对象
  map.put("apple", "新鲜的苹果");      //向集合中添加对象
  map.put("computer", "配置优良的计算机");
  map.put("book", "堆积成山的图书");
  map.put("time", new Date()); 
  String key = "book"; 
  boolean contains = map.containsKey(key);    //判断是否包含指定的键值
  if (contains) {         //如果条件为真
   System.out.println("在Map集合中包含键名" + key); //输出信息
  } else {
   System.out.println("在Map集合中不包含键名" + key);
  }
}

Map.get方法——返回指定键所映射的值

语法 get(Object key))
key:是指定的Map集合中的键名。
该方法返回指定键所映射的值。如果此映射不包含该键的映射关系,则返回null。

public static void main(String[] args) {
  Map map = new HashMap();     //定义Map集合对象
  map.put("apple", "新鲜的苹果");    //向集合中添加对象
  map.put("computer", "配置优良的计算机");
  map.put("book", "堆积成山的图书");
  Object get = map.get("apple");    //获取指定键所映射的值
  if (get instanceof String) {    //判断键值是否为String类型
   String value = (String) get;    //获取指定的value值
   System.out.println("在Map集合中键名apple的键值是:" + value); //将value值输出
  }
}

———————— 我是分割线 —————————-

3. Longest Substring Without Repeating Characters

题目:

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given “abcabcbb”, the answer is “abc”, which the length is 3.

Given “bbbbb”, the answer is “b”, with the length of

Given “pwwkew”, the answer is “wke”, with the length of Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

Approach #1 Brute Force [Time Limit Exceeded]

Intuition

Check all the substring(子字符串) one by one(所有)to see if it has no duplicate character(重复的字符).

Algorithm

Suppose we have a function boolean allUnique(String substring) which will return true if the characters in the substring are all unique(唯一的), otherwise false. We can iterate(迭代) through all the possible substrings of the given string s and call the function allUnique. If it turns out to be true, then we update our answer of the maximum length of substring without duplicate characters.

Now let’s fill the missing parts:

  1. To enumerate(枚举) all substrings of a given string, we enumerate the start and end indices(索引) of them. Suppose the start and end indices are i and j,respectively(分别地). Then we have 0<=i < j <= n (here end index j is exclusive by convention). Thus, using two nested loops(嵌套循环) with i from 0 to n−1 and j from i+1 to n, we can enumerate all the substrings of s.
  2. To check if one string has duplicate characters(重复的字符), we can use a set. We iterate through all the characters in the string and put them into the set one by one. Before putting one character, we check if the set already contains it. If so, we return false. After the loop, we return true.

code:


class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        int ans = 0;
        for (int i = 0 ; i < n ; i++){
            for(int j = i+1;j <= n; j++){
                if(allUnique(s,i,j)) 
                    //如有值,则取最大值
                    ans = Math.max(ans,j-i);
            }
        }
        return ans;
    }

    public boolean allUnique(String s , int start , int end){

        Set<Character> set = new HashSet<>();
        for (int i = start ; i < end ; i++){
            Character ch = s.charAt(i);
            if(set.contains(ch)) return false;
            set.add(ch);
        }
        return true;
    }
}
//run code答案正确,但是测试的时候超时

Complexity Analysis

这里写图片描述

Approach #2 Sliding Window [Accepted]

Algorithm:

The naive approach(朴素的方法) is very straightforward(简单). But it is too slow. So how can we optimize(优化) it?

In the naive approaches(朴素的方法), we repeatedly check a substring to see if it has duplicate character. But it is unnecessary. If a substring s​ij​​ from index i to j−1 is already checked to have no duplicate characters. We only need to check if s[j] is already in the substring s​ij​​ .

To check if a character is already in the substring, we can scan the substring, which leads to an O(n^2) algorithm. But we can do better.

By using HashSet as a sliding window, checking if a character in the current can be done in O(1).

A sliding window(滑动窗口) is an abstract concept commonly used in array/string problems. A window is a range of elements in the array/string which usually defined by the start and end indices, i.e. [i, j)(left-closed, right-open). A sliding window is a window “slides” its two boundaries to the certain direction. For example, if we slide [i, j) to the right by 1 element, then it becomes [i+1, j+1)(left-closed, right-open).

Back to our problem. We use HashSet to store the characters in current window [i, j) (j = i initially(最初)). Then we slide the index j to the right. If it is not in the HashSet, we slide j further. Doing so until s[j] is already in the HashSet. At this point, we found the maximum size of substrings without duplicate characters start with index i. If we do this for all i, we get our answer.

code:

class Solution {
    public int lengthOfLongestSubstring(String s) {

        int n=s.length();
        Set<Character> set = new HashSet<>();
        int ans = 0 ,i = 0 ,j =0 ;

        while( i < n && j < n){

            // 扩展 [ i , j ] 的范围
            //如果s[j]不存在HashSet中,我们滑动 j 进一步
            if(!set.contains(s.charAt(j))){
                set.add(s.charAt(j++));
                ans = Math.max(ans , j-i);
            }
            //知道 s[j] 已存在 HashSet 中,直到没有重复字符串的最大尺寸以索引 i 开始
            //知道遍历了所有的 i 就能得出答案
            else{
                set.remove(s.charAt(i++));
            }
        }
        return ans;
    }
}

Complexity Analysis 复杂度分析

Time complexity : O(2n) = O(n). In the worst case each character will be visited twice by i and j.

Space complexity : O(min(m, n)). Same as the previous approach. We need O(k) space for the sliding window, where k is the size of the Set. The size of the Set is upper bounded by the size of the string n and the size of the charset/alphabet m.

Approach #3 Sliding Window Optimized [Accepted]

The above solution requires at most 2n steps(步骤). In fact, it could be optimized(优化) to require only n steps. Instead of using a set to tell if a character exists or not, we could define a mapping(映射) of the characters to its index(字符到索引). Then we can skip the characters immediately when we found a repeated character.

The reason is that if s[j] have a duplicate in the range [i, j) with index j’​​ , we don’t need to increase i little by little(不需要一点一点的增加 i). We can skip all the elements in the range [ i, j’] and let i to be j’ + 1 directly.

code:Java (Using HashMap)

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length() , ans=0;
        //创建字符(键名)到索引(键值)的映射
        Map<Character , Integer> map = new HashMap<>();

        //扩展 [i,j] 的范围
        for(int j = 0 , i = 0 ; j < n ; j++){

            //当发现重复字符的时候就直接跳过这些字符
            if(map.containsKey(s.charAt(j))){
                i = Math.max( map.get(s.charAt(j)) , i);
            }
            ans = Math.max(ans , j - i + 1);
            map.put(s.charAt(j) , j+1);          
        }
        return ans;
    }
}

Java (Assuming ASCII 128)

The previous implements(工具) all have no assumption (假设)on the charset of the string s.

If we know that the charset is rather small, we can replace the Map with an integer array as direct access table.(直接访问表)

Commonly used tables are:
- int[26] for Letters ‘a’ - ‘z’ or ‘A’ - ‘Z’;
- int[128] for ASCII;
- int[256] for Extended ASCII;

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length() , ans=0;
        //使用直接访问表
        int[] index = new int[128];//当前字符的索引

        //扩展 [i,j] 的范围
        for(int j = 0 , i = 0 ; j < n ; j++){

            //当发现重复字符的时候就直接跳过这些字符
            i = Math.max(index[s.charAt(j)] , i);
            ans = Math.max(ans , j - i + 1);
           index[s.charAt(j)]= j + 1;       
        }
        return ans;
    }
}
Complexity Analysis 复杂度分析
  • Time complexity : O(n). Index j will iterate n times.(指数 j 将迭代n次)

  • Space complexity (HashMap) : O(min(m, n)). Same as the previous approach.

  • Space complexity (Table): O(m). mm is the size of the charset.

友情链接:

  1. csdn-认识优化查询中的Merge Join、Nested Loops和Hash Match

  2. csdn-java枚举类的基本使用

  3. leecode-解题

  4. List和Set的contains()方法实现原理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值