题目
- 给定一个字符串,找出其中不含有重复字符的最长子串的长度
- 例:
- 输入:“abcabcbb”
- 输出:3
解法一:暴力
一开始看到这道题目的时候,很容易地想到了暴力解法,即从头开始进行扫描。
算法步骤:
-
- 从头到尾扫描一次s,用一个数组记录其中不同字符的总数量,存储总不同的字符数。
-
- 从第 i (i从0开始)个字符开始扫描
-
- 不存在重复时,则子串长度加一,j (子字符串的扫描指针)指针右移;扫描到重复时,则开始判断此时的子串长度是否比当前记录的子串长度长,以及为依据判断是否记录当前子串长度,然后母串的指针从 i + 1的位置开始重新扫描。
-
- 重复 2、3步骤,直到 i == s.length 或 当前子串的长度等于给定字符串中的不同字符数,则结束。
这算法相当于每次判断到重复字符时,都需要进行回溯,时间复杂度较大。
- 重复 2、3步骤,直到 i == s.length 或 当前子串的长度等于给定字符串中的不同字符数,则结束。
- 算法复杂度:O(n)~O(n^2),我觉得是O(n ^2),但是跑起来不慢。
package com.company;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// write your code here
int length;
String s;
Scanner in = new Scanner(System.in);
System.out.println("Please input the String");
s = in.nextLine();
length = lengthOfLongestSubstring(s);
System.out.println(length);
}
public static int lengthOfLongestSubstring(String s) {
char[] array = new char[100];
char[] s_change = s.toCharArray();
int k = 0;
int biggest_count = 0;
int length_of_substring = 0;
int length_temp = 0;
int i = 0;
boolean similar = false;
//计算有可能的最大子串数
for (i = 0; i < s_change.length; i++) {
for (int j = 0; j < biggest_count; j++) {
if (array[j] == s_change[i]) {
similar = true;
}
}
if (similar == false) {
array[biggest_count] = s_change[i];
biggest_count++;
} else {
similar = false;
}
}
//寻找最大子串数,当目前扫描的子串数等于最大的可能子串数或扫描完成时,退出。
while (k < s_change.length && length_of_substring < biggest_count) {
for (i = k; i < s_change.length; i++) {
//跟子串进行对比
for (int j = 0; j < length_temp; j++) {
if (array[j] == s_change[i]) {
similar = true;
break;
}
}
//不存在重复时进行加一
if (similar == false) {
array[length_temp] = s_change[i];
length_temp++;
} else//存在重复时的操作
{
similar = false;
break;
}
}
if (i == s_change.length) {
k = i - 1;
}
//选出最长的子串
if (length_temp > length_of_substring) {
length_of_substring = length_temp;
}
length_temp = 0;
k++;
}
return length_of_substring;
}
}
解法二:简单类型的滑窗算法
此算法为后续学习来的,故记录
算法
- 定义的关键数据意义:1.此算法需要两个 int 型变量作为指针,分别作为“窗户”的左右,故定义了 left 和 right。2.其次需要额外开一个数组count [ ] ,大小大于ASCII码表的数,因为后续需要用到count [ ] 数组来记录字符在s中出现的次数,其中,以字符本身的ACSII码作为下标。
-
- 右指针 right 从 s[0] 开始,向右扫描,每扫描到字符时,将字符的ACSII码在count中对应的位置 + 1;
-
- 此时判断该字符的ASCII码在count中记录的数量是否 == 2(等于 2 则说明扫描到了重复的字符):若为2,此时记录该字符的ACSII码(后面判断需要),得到子串长度,判断是否为目前最长;若不为2,则重复 1 步骤,直到扫描完毕。
-
- 将左指针对饮的字符的ACSII码在count中 - 1,左指针右移,直到上面记录的count[ position ] 不为2时,则重复1步骤。
- 算法复杂度:O(n)
package com.company;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
// write your code here
int length;
String s;
Scanner in = new Scanner(System.in);
System.out.println("Please input the String");
s = in.nextLine();
length = lengthOfLongestSubstring(s);
System.out.println(length);
}
//滑窗法寻找最长不重复字符串
public static int lengthOfLongestSubstring(String s)
{
int left = 0,right = 0;
int max_substring_length = 0;
int temp_substring_length = 0;
int []count = new int[150];//用于记录字符出现的次数
char []s_change = s.toCharArray();
int position = 0;
//开始进行扫描
while (right < s_change.length)
{
//以字符的ACSII码作为下标
count[s_change[right]] ++;
//右指针右移
if(count[s_change[right]] == 2)//当出现重复字母时
{
position = s_change[right];
temp_substring_length = right - left;
if(temp_substring_length > max_substring_length)//判断子串长度
{
max_substring_length = temp_substring_length;
}
}
right ++;
//当出现重复的字符时,左指针右移,直到将重复的字符不包括在区间
while (count[position] == 2 )
{
count[s_change[left]] --;
left ++;
}
}
if(right == s_change.length)
{
temp_substring_length = right -left;
if(temp_substring_length > max_substring_length)
{
max_substring_length = temp_substring_length;
}
}
return max_substring_length;
}
}