【算法】【字符串】关联子串
1 题目
给定两个字符串str1和str2,如果字符串str1中的字符,经过排列组合后的字符串中,只要有一个字符串是str2的子串,则认为str1是str2的关联子串。
若str1是str2的关联子串,请返回子串在str2的起始位置;
若不是关联子串,则返回-1。
输入描述:输入两个字符串,分别为题目中描述的str1、str2。
备注:输入的字符串只包含小写字母;两个字符串的长度范围[1, 100000]之间;
输出描述:
若str1是str2的关联子串,请返回子串在str2的起始位置;
若不是关联子串,则返回-1。
若str2中有多个str1的组合子串,请返回最小的起始位置。
示例 1:
abc efghicbaiii
5
示例2:
abc efghiccaiii
-1
2 解答
这道算法,我刚开始的时候我老想着 str1 的排列组合那么多,得到它所有的排列组合然后逐个判断是不是 str2 的子串,再从中选取一个最小的索引位置返回。但是所有的排列组合,那家伙字符串稍微长点那家伙量级岂不是很大貌似是 str1 长度的阶乘。所以我们换个角度去想,因为排列组合所以是任意的,但长度是固定的,我们是不是先分析下 str1 的组成比如是 a1b2c3d2 表示 a字符有1个、b字符2个、c字符3个、d字符2个,那么我们是不是在 str2 中每次取这样相等的长度,然后只要凑出这样的组成是不是就表示是关联子串了吧。我们用代码实现一下:
// 关联子串 public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 字符串1 String str1 = "abc"; // 字符串2 String str2 = "efghiccaiii"; // 默认返回结果 int res = -1; int str1Len = str1.length(); int str2Len = str2.length(); // 边界判断 if (str1Len > 0 && str1Len <= str2Len) { // 存储字符构成 int[] arr = new int[26]; // 先分析下 str1 for (int i = 0; i < str1Len; i++) { int index = str1.charAt(i) - 'a'; arr[index] += 1; } // str1 的组成 StringBuilder str1Builder = new StringBuilder(); for (int i = 0; i < arr.length; i++) { if (arr[i] > 0) { str1Builder.append((char)('a' + i) + "" + arr[i]); } } // 从 str2 每次遍历 str1 的长度出来 看看它的组成 for (int i = 0; i < str2Len - str1Len; i++) { // 重置 for (int j = 0; j < arr.length; j++) { arr[j] = 0; } // 分析子串组成 for (int j = i; j < i + str1Len; j++) { int index = str2.charAt(j) - 'a'; arr[index] += 1; } StringBuilder subBuilder = new StringBuilder(); for (int j = 0; j < arr.length; j++) { if (arr[j] > 0) { subBuilder.append((char)('a' + j) + "" + arr[j]); } } // 判断是否组成跟 str1 的相等,相等直接返回并退出循环 if (subBuilder.toString().equals(str1Builder.toString())) { res = i; break; } } // 打印结果 System.out.println(res); } }
这题挺好,又提供了一种思考的角度,与其大费周折的分析所有的排列组合,不如直接分析其组成,然后等长度的分析另一个字符串即可。