String 字符串作为另一个字符串的子字符串的排列
给定一个字符串a和另一个字符串B。找出B的任何置换是否作为a的子字符串存在String 字符串作为另一个字符串的子字符串的排列,string,algorithm,substring,permutation,String,Algorithm,Substring,Permutation,给定一个字符串a和另一个字符串B。找出B的任何置换是否作为a的子字符串存在 My solution-> if length(A)=n and length(B)=m I did this in 0((n-m+1)*m) by sorting B and then checking A with window size of m each time. 比如说, 如果A=“百科全书” 如果B=“dep”,则返回true,因为ped是dep的置换,ped是a的子串 My solution
My solution->
if length(A)=n and length(B)=m
I did this in 0((n-m+1)*m) by sorting B and then checking A
with window size of m each time.
比如说,
如果A=“百科全书”
如果B=“dep”,则返回true,因为ped是dep的置换,ped是a的子串
My solution->
if length(A)=n and length(B)=m
I did this in 0((n-m+1)*m) by sorting B and then checking A
with window size of m each time.
我需要找到一个更好更快的解决方案。在j|u random|u hacker在评论中提出的算法的基础上,可以在
O(|a |+|B |)中找到匹配项,如下所示:(注意:在整个过程中,我们使用|a |
来表示a的长度)
创建一个整数数组count
,其域为字母表大小,初始化为all0
s
将距离设置为0
对于B
中的每个字符Bi
:
- 减量
计数[Bi]
- 如果
count[Bi]
的上一次计数为0
,也应增加距离
对于A
中的每个字符Ai
:
- 增量
计数[Ai]
- 如果
i
大于|B |
减量计数[Ai-|B |]
- 对于修改的两个<代码>计数
值中的每一个,如果上一个值是<代码>0
,则增加<代码>距离,如果新值是<代码>0,则减少<代码>距离
distance
为0
,则已找到匹配项注意:j|u random_hacker提出的算法也是
O(|A |+|B])
,因为将freqA
与freqB
进行比较的成本是O(|字母表|)
,这是一个常数。然而,上述算法将比较成本降低到一个小常数。此外,通过使用未初始化数组的标准技巧,即使字母表的大小不是恒定的,理论上也可以实现这一点。这个问题有一个更简单的解决方案,可以在线性时间内完成
这里:n=A.size(),m=B.size()
想法是使用散列
首先,我们散列字符串B的字符
假设:B=“dep”
- 散列B['d']=1李>
- 散列B['e']=1李>
- 散列B['p']=1李>
- 赢['e']=1
- 赢['n']=1
- 赢['c']=1
- 赢['n']=1
- 赢['c']=1
- 赢['y']=1
- 赢['p']=1李>
- 赢['e']=1李>
- 赢['d']=1李>
这与哈希_B数组相同。所以,打印“SUCCESS”并退出。如果我只需要担心ASCII字符,它可以在
O(n)
时间和O(1)
空间内完成。我的代码也会打印出排列,但是可以很容易地修改,只需在第一个实例中返回true即可。代码的主要部分位于printAllPermutations()
方法中。以下是我的解决方案:
一些背景
这是我提出的一个解决方案,它有点类似于拉宾卡普算法背后的想法。在我理解算法之前,我将对其背后的数学进行如下解释:
1 -> 1st prime
2 -> 2nd prime
3 -> 3rd prime
...
n -> nth prime
设S={A_1,…,A_n}是只包含素数的大小n的列表。让S中的数字之和等于某个整数Q。然后,S是唯一可能的大小为N的完全素数多集,其元素可以和Q
正因为如此,我们知道我们可以将每个字符映射到一个素数。我提议的地图如下:
1 -> 1st prime
2 -> 2nd prime
3 -> 3rd prime
...
n -> nth prime
如果我们这样做(我们可以这样做,因为ASCII只有256个可能的字符),那么我们就很容易在较大的字符串B中找到每个排列
算法:
我们将做以下工作:
1:计算A中每个字符映射到的素数之和,我们称之为smallHash
2:创建2个索引(righti和lefti)。righti初始化为零,lefti初始化为A的大小
ex: | |
v v
"abcdabcd"
^ ^
| |
3:创建一个变量currHash,并将其初始化为B中每个字符对应的质数之和,介于(包括)righti和lefti-1之间
4:按1迭代righti和lefti,每次更新currHash时,从不再在范围内的字符(lefti-1)中减去映射的素数,并添加与刚刚添加到范围内的字符(righti)对应的素数
5:每次currHash等于smallHash时,范围中的字符必须是排列。所以我们把它们打印出来
6:继续,直到到达B的末端。(当righti等于B的长度时)
此解决方案在O(n)
时间复杂度和O(1)
空间中运行
实际代码:
公共
#include <string>
bool is_permuted_substring(std::string_view input_string,
std::string_view search_string) {
if (search_string.empty()) {
return true;
}
if (search_string.length() > input_string.length()) {
return false;
}
int character_frequencies[256]{};
auto distance = search_string.length();
for (auto c : search_string) {
character_frequencies[(uint8_t)c]++;
}
for (auto i = 0u; i < input_string.length(); ++i) {
auto& cur_frequency = character_frequencies[(uint8_t)input_string[i]];
if (cur_frequency > 0) distance--;
cur_frequency--;
if (i >= search_string.length()) {
auto& prev_frequency = ++character_frequencies[(
uint8_t)input_string[i - search_string.length()]];
if (prev_frequency > 0) {
distance++;
}
}
if (!distance) return true;
}
return false;
}
int main() {
auto test = [](std::string_view input, std::string_view search,
auto expected) {
auto result = is_permuted_substring(input, search);
printf("%s: is_permuted_substring(\"%.*s\", \"%.*s\") => %s\n",
result == expected ? "PASS" : "FAIL", (int)input.length(),
input.data(), (int)search.length(), search.data(),
result ? "T" : "F");
};
test("", "", true);
test("", "a", false);
test("a", "a", true);
test("ab", "ab", true);
test("ab", "ba", true);
test("aba", "aa", false);
test("baa", "aa", true);
test("aacbba", "aab", false);
test("encyclopedia", "dep", true);
test("encyclopedia", "dop", false);
constexpr char negative_input[]{-1, -2, -3, 0};
constexpr char negative_search[]{-3, -2, 0};
test(negative_input, negative_search, true);
return 0;
}
class Test {
public static boolean isPermuted(int [] asciiA, String subB){
int [] asciiB = new int[26];
for(int i=0; i < subB.length();i++){
asciiB[subB.charAt(i) - 'a']++;
}
for(int i=0; i < 26;i++){
if(asciiA[i] != asciiB[i])
return false;
}
return true;
}
public static void main(String args[]){
String a = "abbc";
String b = "cbabadcbbabbc";
int len = a.length();
int [] asciiA = new int[26];
for(int i=0;i<a.length();i++){
asciiA[a.charAt(i) - 'a']++;
}
int lastSeenIndex=0;
for(int i=0;i<b.length()-len+1;i++){
String sub = b.substring(i,i+len);
System.out.printf("%s,%s\n",sub,isPermuted(asciiA,sub));
} }
}
public int PermutationOfPatternInString(string text, string pattern)
{
int matchCount = 0;
Dictionary<char, int> charCount = new Dictionary<char, int>();
int patLen = pattern.Length;
foreach (char c in pattern)
{
if (charCount.ContainsKey(c))
{
charCount[c]++;
}
else
{
charCount.Add(c, 1);
}
}
var subStringCharCount = new Dictionary<char, int>();
// loop through each character in the given text (string)....
for (int i = 0; i <= text.Length - patLen; i++)
{
// check if current char and current + length of pattern-th char are in the pattern.
if (charCount.ContainsKey(text[i]) && charCount.ContainsKey(text[i + patLen - 1]))
{
string subString = text.Substring(i, patLen);
int j = 0;
for (; j < patLen; j++)
{
// there is no point going on if this subString doesnt contain chars that are in pattern...
if (charCount.ContainsKey(subString[j]))
{
if (subStringCharCount.ContainsKey(subString[j]))
{
subStringCharCount[subString[j]]++;
}
else
{
subStringCharCount.Add(subString[j], 1);
}
}
else
{
// if any of the chars dont appear in the subString that we are looking for
// break this loop and continue...
break;
}
}
int x = 0;
// this will be true only when we have current subString's permutation count
// matched with pattern's.
// we need this because the char count could be different
if (subStringCharCount.Count == charCount.Count)
{
for (; x < patLen; x++)
{
if (subStringCharCount[subString[x]] != charCount[subString[x]])
{
// if any count dont match then we break from this loop and continue...
break;
}
}
}
if (x == patLen)
{
// this means we have found a permutation of pattern in the text...
// increment the counter.
matchCount++;
}
subStringCharCount.Clear(); // clear the count map.
}
}
return matchCount;
}
[TestCase("encyclopedia", "dep", 1)]
[TestCase("cbabadcbbabbcbabaabccbabc", "abbc", 7)]
[TestCase("xyabxxbcbaxeabbxebbca", "abbc", 2)]
public void PermutationOfStringInText(string text, string pattern, int expectedAnswer)
{
int answer = runner.PermutationOfPatternInString(text, pattern);
Assert.AreEqual(expectedAnswer, answer);
}
public static void main(String[] args) {
String pattern = "dep";
String text = "encyclopedia";
System.out.println(isPermutationAvailable(pattern, text));
}
public static boolean isPermutationAvailable(String pattern, String text) {
if (pattern.length() > text.length()) {
return false;
}
int[] patternMap = new int[26];
int[] textMap = new int[26];
for (int i = 0; i < pattern.length(); i++) {
patternMap[pattern.charAt(i) - 'a']++;
textMap[text.charAt(i) - 'a']++;
}
int count = 0;
for (int i = 0; i < 26; i++) {
if (patternMap[i] == textMap[i]) {
count++;
}
}
for (int i = 0; i < text.length() - pattern.length(); i++) {
int r = text.charAt(i + pattern.length()) - 'a';
int l = text.charAt(i) - 'a';
if (count == 26) {
return true;
}
textMap[r]++;
if (textMap[r] == patternMap[r]) {
count++;
}
else if (textMap[r] == patternMap[r] + 1) {
count--;
}
textMap[l]--;
if (textMap[l] == patternMap[l]) {
count++;
}
else if (textMap[l] == patternMap[l] - 1) {
count--;
}
}
return count == 26;
}
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main ()
{
string shortone = "abbc";
string longone ="cbabadcbbabbcbabaabccbabc";
int s_length = shortone.length ();
int l_length = longone.length ();
string sub_string;
string unsorted_substring; // only for printing
// sort the short one
sort (shortone.begin (), shortone.end ());
if (l_length > s_length)
{
for (int i = 0; i <= l_length - s_length; i++){
sub_string = "";
//Move the window
sub_string = longone.substr (i, s_length);
unsorted_substring=sub_string;
// sort the substring window
sort (sub_string.begin (), sub_string.end ());
if (shortone == sub_string)
{
cout << "substring is :" << unsorted_substring << '\t' <<
"match found at the position: " << i << endl;
}
}
}
return 0;
}