針對 M 個查詢,反轉給定字串的範圍
在這個問題中,我們將根據陣列值對給定的字串執行 M 次反轉查詢。
解決該問題的樸素方法是根據給定的陣列值反轉每個字串片段。
最佳化方法利用了以下邏輯:當我們對同一個子字串反轉兩次時,我們會得到原始字串。
問題陳述 - 我們給定一個包含字母字元的 alpha 字串。此外,我們還給定一個大小為 M 的 arr[] 陣列,其中包含正整數。我們需要對給定的字串執行 M 個操作並返回最終字串。
在每個操作中,我們需要獲取 arr[i] 並反轉從 arr[i] 到 N − arr[i] + 1 的子字串。
示例
輸入
alpha = "pqrst"; arr = {2, 1};
輸出
tqrsp
解釋
執行第一個查詢後,字串變為“psrqt”。
執行第二個查詢後,我們得到“tqrsp”。
輸入
− alpha = "pqrst"; arr = {1, 1};
輸出
− ‘pqrst’
解釋 - 如果我們對同一個查詢執行偶數次,我們會得到相同的字串。
輸入
− alpha = "pqrst"; arr = {1, 1, 1};
輸出
− ‘tsrqp’
解釋 - 如果我們對同一個查詢執行奇數次,我們會得到字串的反轉。
方法 1
在這種方法中,我們將使用 reverse() 方法反轉子字串。我們將使用給定的查詢獲取起始和結束指標,並反轉給定字串的子字串。
演算法
步驟 1 - 開始遍歷查詢陣列。
步驟 2 - 使用 arr[p] − 1 初始化“left”變數。
步驟 3 - 使用 str_len − arr[p] + 1 初始化“right”變數。
步驟 4 - 使用 reverse() 方法反轉從左指標到右指標的子字串。
示例
#include <bits/stdc++.h> using namespace std; void reverseStrings(string &alpha, int str_len, vector<int> &arr, int arr_len){ // Traverse all queries for (int p = 0; p < arr_len; p++){ // Get starting pointer int left = arr[p] - 1; // Ending pointer int right = str_len - arr[p] + 1; // Reverse the string reverse(alpha.begin() + left, alpha.begin() + right); } } int main(){ int str_len = 5; string alpha = "pqrst"; int arr_len = 2; vector<int> arr = {2, 1}; reverseStrings(alpha, str_len, arr, arr_len); cout << "The string after performing queries is " << alpha << endl; return 0; }
輸出
The string after performing queries is tqrsp
時間複雜度 - 反轉子字串 M 次的時間複雜度為 O(N*M)。
空間複雜度 - O(1),因為我們沒有使用任何動態空間。
方法 2
在這種方法中,我們將計算特定索引以及在給定查詢中包含反轉的次數。如果任何索引在偶數次包含在反轉中,我們不需要反轉它。如果任何索引在所有給定查詢中奇數次包含在反轉中,我們需要反轉特定索引處的字元。
演算法
步驟 1 - 初始化長度等於字串長度的“cnt”列表,並將其值初始化為 0,以儲存特定索引包含在反轉中的次數。
步驟 2 - 遍歷給定查詢的陣列,並根據當前查詢獲取字串的左指標和右指標。
步驟 3 - 還執行 changeRange() 函式以根據當前查詢的左指標和右指標更新“cnt”列表。
步驟 3.1 - 在 changeRange() 函式中,增加“cnt”列表中“left”索引處的值。
步驟 3.2 - 減小“cnt”列表中所有位於“right + 1”指標右側的值。
這裡,我們需要在 [left, right] 範圍內將“cnt”列表的所有值加 1。因此,我們只將 cnt[left] 加 1,因為獲取字首和會將所有位於“left”索引右側的值加 1。此外,我們不希望增加 [right, str_len] 索引之間的 cnt 值,因此我們已經將其減 1,因為字首和會將其加 1。
步驟 4 - 接下來,執行 getPrefixSum() 函式以計算“cnt”列表的字首和。
步驟 4.1 - 在 getPrefixSum() 函式中,遍歷字串並將前一個元素的值新增到當前元素。
步驟 5 - 接下來,以相反的順序遍歷“cnt”列表。如果當前元素為奇數,則將其追加到“tmp”字串。
步驟 6 - 使用 0 初始化“p”和“q”,以原始順序遍歷“cnt”列表。
步驟 7 - 如果“cnt”列表中的當前元素為奇數,則使用 tmp[q] 更新 alpha[p]。
步驟 8 - 最後,返回 alpha 字串。
示例
#include <iostream> #include <vector> using namespace std; void changeRange(vector<int>& cnt, int left, int right) { // Increase the value of the left index cnt[left]++; // Decrement value for all indexes after the right index if (right + 1 < cnt.size()) cnt[right + 1]--; } void getPrefixSum(vector<int>& cnt) { // Calculate prefix sum for (int p = 1; p < cnt.size(); p++) { cnt[p] += cnt[p - 1]; } } string reverseStrings(string alpha, int str_len, vector<int>& arr, int arr_len) { vector<int> cnt(str_len, 0); // Traverse the array for (int p = 0; p < arr_len; p++) { int left = arr[p] <= (str_len + 1) / 2 ? arr[p] - 1 : str_len - arr[p]; int right = arr[p] <= (str_len + 1) / 2 ? str_len - arr[p] : arr[p] - 1; // Changes index ranges between left and right changeRange(cnt, left, right); } getPrefixSum(cnt); string tmp; // Store characters with the odd reversal in the reverse order in the tmp string for (int p = cnt.size() - 1; p >= 0; p--) { if (cnt[p] % 2 != 0) tmp.push_back(alpha[p]); } int p = 0, q = 0; // For even reversal, pick the character from the original string. // For odd reversal, pick the character from the temp string. for (p = 0; p < cnt.size(); p++) { if (cnt[p] % 2 != 0) alpha[p] = tmp[q++]; } // Answer string return alpha; } int main() { int str_len = 5; string alpha = "pqrst"; int arr_len = 2; vector<int> arr = { 2, 1 }; alpha = reverseStrings(alpha, str_len, arr, arr_len); cout << "The string after performing queries is: " <<alpha << endl; return 0; }
輸出
The string after performing queries is: tqrsp
時間複雜度 - O(M*N + N),其中 O(M*N) 用於根據查詢更新“cnt”列表,O(N) 用於更新給定字串。
空間複雜度 - O(N),用於使用“cnt”列表。
在第一種方法中,我們使用 reveres() 方法對給定字串執行所有查詢。在第二種方法中,我們使用字首和技術來計算特定索引包含在反轉中的次數。