检查由给定字符组成的子序列对Q查询是否相同
- 最后更新: 2022年5月10日
给定一个包含N个字符串的数组 arr[]和Q个查询,其中每个查询都包含一些字符,任务是检查对于每个查询,由该查询的所有字符出现的所有字符串组成的子序列是否相同。
子序列是指在不改变剩余元素顺序的情况下,通过删除部分或全部元素从给定序列中派生出来的序列。
例子。
**输入:**arr[] = {"accbad", "abcacd", "cacbda"}, queries[] = {{'a'}, {'a', 'b'}, {'a', 'd'}, {'b', 'c', 'd'}, {'a', 'b', 'd'}
输出。
True
True
False
True
False
False
**解释。**所有的字符串都只用'a'这个字符生成子序列 "aa"。arr[1]和arr[2]生成子序列 "aad",但arr[3]只用'a'和'd'生成子序列 "ada",由于子序列不匹配,打印出false。
所有字符串都只用'b'和'd'生成子序列 "bd "
arr[1]和arr[3]生成子序列 "ccbd",但是arr[2]只用'b'、'c'和'd'生成子序列 "bccd",所以打印 "false"。
arr[1]和arr[2]产生子序列 "abad",但arr[3]只用'a'、'b'和'd'的字符产生子序列 "abda"。**输入:**arr[] = {"adacb", "aacbd"}, queries[] = {{'a', 'b', 'c'}}
**输出。**真。
解释。两个字符串的子序列都是 "aacb"。
**天真方法。**对于每个查询,生成包含该查询中所有字符出现的子序列,并检查它们是否相同。
时间复杂度。 O(Q * N * M),其中M是每个字符串的平均长度。
辅助空间。O(1)
使用记忆化的高效方法。这个问题可以根据以下想法有效解决。
如果一个查询的所有字符的相对位置在所有字符串中都是相同的,那么该子序列在所有字符串中是共同的。
任何一个字符(例如字符ch)的相对位置,都可以通过查找查询中所有字符的频率,直到ch的索引,如果它们是相同的,那么它们的相对位置也是相同的。
按照下面提到的步骤来实现这个想法。
- 创建一个3维数组(例如elements[]),以存储到每个索引的字符串的每个字符的频率。
- 遍历所有的字符串,找到频率并存储起来。
- 现在检查一个查询的字符的相对位置。
- 为了有效地实现这一点,在字符串数组中进行迭代,比较相邻的字符串并做以下工作。
- 找出每个字符相对于英语字母表中其他每个字符的相对位置(相对位置是通过检查直到该索引的所有其他字符的频率来检查的)。
- 如果两个字符串中的某个字符(例如ch)的频率不一样,则将导致不匹配的字符添加到一个集合中。(不匹配意味着频率计数不相同)
- 检查发现的不匹配是否与查询的字符相同。
- 如果是,那么形成的子序列就不一样。所以返回false。
- 迭代结束后,如果没有返回 "false",那么形成的子序列就是相同的。
为了更好地理解,请看下面的插图。
插图。
假设arr[] = {"adacb", "aacbd"}, queries[] = {{'a', 'b', 'c'}}.
arr[0]和arr[1]的所有字符的频率显示如下
arr[0] =
'a'
'd' 'a' 'c' 'b' a 1 1 2 2 2 b 0 0 0 0 1 c 0 0 0 1 1 d 0 1 1 1 1 arr[1] =
'a'
'a' 'c' 'b' 'd' a 1 2 2 2 2 b 0 0 0 1 1 c 0 0 1 1 1 d 0 0 0 0 1 对于'a':
=> 第二个'a'的相对位置不一样。
=> 在第一个字符串中,第二个'a'前面多了一个'd'。
=> 所以不匹配集是**{ 'd' }**。对于'b':
=> 'b'的相对位置不一样。
=> 在第一个字符串之前多了一个'd'。
=> 所以不匹配集是**{ 'd' }**。对于'c':
=> 'c'的相对位置不一样。
=> 在第一个字符串中,前面多了一个'd'。
=> 所以错配集是**{ 'd' }**。现在,没有一个错位集包含与查询中存在的相同的字符。所以形成的子序列对字符串来说是一样的。
下面是上述方法的实现。
爪哇
// Java code to implement the approachimport java.io.*;import java.util.*;public class SubsequenceQueries {// Function to check if// the subsequences formed are the samepublic static ArrayList<Boolean>isPossible(int N, ArrayList<String> arr,int Q,String queries[]){// For 26 lettersfinal int numElements =26;// Generate prefix sums (for each letter)// for each stringArrayList<int[][]> elements=new ArrayList<>();for (int i =0; i < N; i++) {// Create a new prefix sum array// and add it to the array listelements.add(new int[arr.get(i).length()][numElements]);int[][] tmp = elements.get(i);// Build the prefix sum// at each position in the stringfor (int j =0; j < arr.get(i).length();j++) {for (int k =0; k < numElements;k++) {if (j !=0)tmp[j][k] = tmp[j -1][k];}// ASCII to int conversion// for lowercase letterstmp[j][arr.get(i).charAt(j) -97]++;}}// Generate the set of characters// which are necessary to remove.// Each mapping is the set// corresponding of characters// which need to be removed.// for each letter in order for a// subsequence to be generated.HashMap<Integer, Set<Integer> > requiredRemovals=new HashMap<>();for (int i =0; i < numElements; i++)requiredRemovals.put(i,new HashSet<Integer>());// Iterate over all the characters// (in the alphabet in this case)for (int i =0; i < numElements; i++) {// For each character,// go through all M strings// to generate prefix sumsfor (int j =1; j < N; j++) {// String a stores// the previous string (j-1)// string b stores// the current string (j)String a = arr.get(j -1);String b = arr.get(j);// Store the prefix sums// for strings a and bint[][] elements1= elements.get(j -1);int[][] elements2= elements.get(j);int p1 =0;int p2 =0;// Check if the lengths of characters// differ; if they do, then// no valid subsequence// with that character can be generated.// So for all other letters,// add that character to its Set.// Otherwise, check the count// of each character// at every position where// letter i appears in both stringsif (elements1[a.length() -1][i]!= elements2[b.length() -1][i]) {for (int key :requiredRemovals.keySet())requiredRemovals.get(key).add(i);}else {// Iterate through both strings// using p1 for all characters// in the first string// and p2 for all characters// in the second stringwhile (p1 < a.length()&& p2 < b.length()) {// Skip to the next occurence of// character i in string awhile (p1 < a.length()&& a.charAt(p1)-97!= i) {p1++;}// Skip to the next occurence of// character i in string bwhile (p2 < b.length()&& b.charAt(p2)-97!= i) {p2++;}// Compare the count of each// character to check if they match// in both stringsif (p1 < a.length()&& p2 < b.length()) {// Iterate over// their prefix sumsfor (int k =0;k < numElements;k++) {if (elements1[p1][k]!= elements2[p2][k])requiredRemovals.get(i).add(k);}}p1++;p2++;}}}}ArrayList<Boolean> res=new ArrayList<Boolean>();// Read in Q queriesfor (int i =0; i < Q; i++) {// st = new StringTokenizer(br.readLine());// String q = st.nextToken();Set<Integer> union=new HashSet<Integer>();// generate a combined set of all characters// which must be removed for a valid subsequence// to be created with the query stringfor (char c : queries[i].toCharArray())union.addAll(requiredRemovals.get(c -97));boolean ans =true;// if there are any contradictions in the query,// then the answer will be falsefor (char c : queries[i].toCharArray()) {if (union.contains(c -97))ans =false;}res.add(ans);}return res;}// Driver codepublic static void main(String[] args)throws IOException{int N =3;ArrayList<String> arr=new ArrayList<String>();arr.add("accbad");arr.add("abcacd");arr.add("cacbda");int Q =6;String queries[]=new String[6];queries[0] ="a";queries[1] ="ab";queries[2] ="ad";queries[3] ="bd";queries[4] ="bcd";queries[5] ="abd";// Function callArrayList<Boolean> ans= isPossible(N, arr, Q, queries);for (boolean val : ans)System.out.println(val);}} |
输出
true
true
false
true
false
false
**时间复杂度。**O(C2* N + Q * N)
**辅助空间。**O(C2* N) 其中C = 26
我的个人笔记arrow_drop_up
保存