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);// 字符串1String str1 = "abc";// 字符串2String str2 = "efghiccaiii";// 默认返回结果int res = -1;int str1Len = str1.length();int str2Len = str2.length();// 边界判断if (str1Len > 0 && str1Len <= str2Len) {// 存储字符构成int[] arr = new int[26];// 先分析下 str1for (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);} }
这题挺好,又提供了一种思考的角度,与其大费周折的分析所有的排列组合,不如直接分析其组成,然后等长度的分析另一个字符串即可。