如何解决查找第一个和最后一个字符重复的子串有多少个的快速方法
这是关于我创建的子字符串的问题。我想知道如何为这个问题实现一个 O(nlog(n))
解决方案,因为这种幼稚的方法非常简单。这是怎么回事。您有一个字符串 S
。 S
有很多子串。在某些子字符串中,第一个字符和最后一个字符出现多次。找出第一个和最后一个字符出现多次的子串有多少个。
Input: "ABCDCBE"
Expected output: 2
Explanation: "BCDCB" and "CDC" are two such substrings
那个测试用例解释只有“BCDCB”和“CDC”,其中第一个和最后一个字符是相同的。
除了“ABABCAC”作为第一个字符“A”出现3次而最后一个字符“C”出现两次的子字符串的示例案例之外,还有另一种情况。 “AAAABB”也是另一个子串。
“AAAAB”不满足。
我了解到的 O(nlog(n))
可能有助于也可能不会有助于解决方案是二叉索引树。二叉索引树可以以某种方式用于解决这个问题。还有排序和二分搜索,但首先我想特别关注二叉索引树。
我正在寻找 O(n log(n))
或更高的空间复杂度。
字符也是 UTF-16
解决方法
我的解决方案的要点如下:
迭代输入数组,并为每个位置计算在该位置结束的“有效”子串的数量。这些值的总和是有效子串的总数。我们通过使用二叉索引树计算当前位置之前的子字符串的有效起始数量来实现这一点。
现在是完整的细节:
当我们遍历数组时,我们认为当前元素是子字符串的结尾,我们说有效开始的位置是那些它的值再次出现在it之间的位置,以及我们目前正在迭代的位置。 (即,如果子字符串开头的值在其中至少出现两次)
例如:
current index V
data = [1,2,3,4,1,2]
valid = [1,0]
0 1 2 3 4 5 6 7
第一个 1
(在索引 0
处)是一个有效的开始,因为在它之后,但在当前索引之前还有另一个 1
(在索引 4
处) (索引 6
)。
现在,计算出现在当前索引之前的有效开始的数量给了我们一些非常接近我们想要的结果,除了我们可能会抓取一些子字符串的最后一个值没有两次出现的子字符串(即我们目前正在迭代的那个)
例如:
current index V
data = [1,0]
0 1 2 3 4 5 6 7
^--------^
这里,4 被标记为有效的开始(因为在它之后还有另一个 4
),但对应的子串没有两个 3
。
为了解决这个问题,我们将只考虑当前值之前出现的有效启动。 (这意味着子字符串将同时包含当前值和它之前的出现,因此,最后一个元素将在子字符串中至少出现两次)
伪代码如下:
fn solve(arr) {
answer := 0
for i from 1 to length(arr) {
previous_index := find_previous(arr,i)
if there is a previous_index {
arr[previous_index].is_valid_start = true
answer += count_valid_starts_up_to_and_including(arr,previous_index)
}
}
return answer
}
为了有效地实现这些操作,我们使用哈希表来查找值的前一个位置,并使用二叉索引树 (BIT) 来跟踪和计算有效位置。
因此,更充实的伪代码看起来像
fn solve(arr) {
n := length(arr)
prev := hash_table{}
bit := bit_indexed_tree{length = n}
answer := 0
for i from 1 to length(arr) {
value := arr[i]
previous_index := prev[value]
if there is a previous_index {
bit.update(previous_index,1)
answer += bit.query(previous_index)
}
prev[value] = i
}
return answer
}
最后,由于伪代码并不总是足够的,这里是在 C++ 中的一个实现,其中控制流有点模糊,以确保有效使用 std::unordered_map
(C++ 的内置哈希表)
class Bit {
std::vector<int> m_data;
public:
// initialize BIT of size `n` with all 0s
Bit(int n);
// add `value` to index `i`
void update(int i,int value);
// sum from index 0 to index `i` (inclusive)
int query(int i);
};
long long solve (std::vector<int> const& arr) {
int const n = arr.size();
std::unordered_map<int,int> prev_index;
Bit bit(n);
long long answer = 0;
int i = 0;
for (int value : arr) {
auto insert_result = prev_index.insert({value,i});
if (!insert_result.second) { // there is a previous index
int j = insert_result.first->second;
bit.update(j,1);
answer += bit.query(j);
insert_result.first->second = i;
}
++i;
}
return answer;
}
编辑:为了透明,这里是我用来测试此代码的 Fenwick 树实现
struct Bit {
std::vector<int> m_data;
Bit(int n) : m_data(n+2,0) { }
int query(int i) {
int res = 0;
for(++i; i > 0; i -= i&-i) res += m_data[i];
return res;
}
void update(int i,int x) {
for(++i; i < m_data.size(); i += i&-i) m_data[i] += x;
}
};
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。