检查由给定字符形成的子序列对Q查询是否相同

66 阅读6分钟

检查由给定字符组成的子序列对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'
a11222
b00001
c00011
d01111

arr[1] =

'a'

'a''c''b''d'
a12222
b00011
c00111
d00001

对于'a':
=> 第二个'a'的相对位置不一样。
=> 在第一个字符串中,第二个'a'前面多了一个'd'。
=> 所以不匹配集是**{ 'd' }**。

对于'b':
=> 'b'的相对位置不一样。
=> 在第一个字符串之前多了一个'd'。
=> 所以不匹配集是**{ 'd' }**。

对于'c':
=> 'c'的相对位置不一样。
=> 在第一个字符串中,前面多了一个'd'。
=> 所以错配集是**{ 'd' }**。

现在,没有一个错位集包含与查询中存在的相同的字符。所以形成的子序列对字符串来说是一样的

下面是上述方法的实现。

爪哇

// Java code to implement the approach
import java.io.*;
import java.util.*;
public class SubsequenceQueries {
// Function to check if
// the subsequences formed are the same
public static ArrayList<Boolean>
isPossible(int N, ArrayList<String> arr,int Q,
String queries[])
{
// For 26 letters
final int numElements =26;
// Generate prefix sums (for each letter)
// for each string
ArrayList<int[][]> elements
=new ArrayList<>();
for (int i =0; i < N; i++) {
// Create a new prefix sum array
// and add it to the array list
elements.add(new int[arr.get(i).length()]
[numElements]);
int[][] tmp = elements.get(i);
// Build the prefix sum
// at each position in the string
for (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 letters
tmp[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 sums
for (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 b
int[][] 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 strings
if (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 string
while (p1 < a.length()
&& p2 < b.length()) {
// Skip to the next occurence of
// character i in string a
while (p1 < a.length()
&& a.charAt(p1)
-97
!= i) {
p1++;
}
// Skip to the next occurence of
// character i in string b
while (p2 < b.length()
&& b.charAt(p2)
-97
!= i) {
p2++;
}
// Compare the count of each
// character to check if they match
// in both strings
if (p1 < a.length()
&& p2 < b.length()) {
// Iterate over
// their prefix sums
for (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 queries
for (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 string
for (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 false
for (char c : queries[i].toCharArray()) {
if (union.contains(c -97))
ans =false;
}
res.add(ans);
}
return res;
}
// Driver code
public 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 call
ArrayList<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

保存