執行給定操作後不同可能字串的計數


確定透過對字串執行一組給定操作可以獲得的唯一字串的數量是計算機科學和數學中的一個常見挑戰。可以對字串執行多種操作,包括字元刪除、交換或字串反轉。目標是計算透過這些操作可以實現的不同輸出字串的總數,而不管其順序如何。解決此問題的技術包括動態規劃、遞迴和組合數學等——具體取決於所執行的特定操作的性質。

方法

為了計算執行給定操作後不同的可能字串,可以使用以下方法:

  • 暴力法。

  • 集合法。

  • 動態規劃。

  • 組合方法。

方法 1:暴力法

此函式允許您建立任何可能透過執行指定過程而產生的字串。然後,計算獲得的不同字串的總數。對於大型輸入,此方法可能很耗時且效率低下。

語法

此方法可以遵循以下步驟:

string_count = 0
for operation_combination in all_possible_combinations(operations_list):
   new_string = apply_operations(original_string, operation_combination)
   if is_distinct(new_string):
   string_count += 1
return string_count

演算法

步驟 1 - 從頭建立一個集合來儲存不同的字串。

步驟 2 - 生成每個可以透過給定過程生成的可能字串。

步驟 3 - 驗證每個生成的字串是否已存在於不同字串的集合中。

步驟 4 - 如果字串不在集合中,則需要將其新增到集合中。

驟 5 - 繼續執行步驟 2-4,直到建立並驗證了每個可能的字串。

步驟 6 - 將不同可能字串的數量作為唯一字串集合的長度返回。

示例 1

我們有一個 C++ 的暴力法示例:

在此示例中,我們將從將輸入字串 s 作為向量的起始值開始。透過更改向量中每個字串中的字元來執行給定的操作。我們重複此過程 k 次,將生成的每個字串儲存在向量中。最後,我們使用 unique 函式對向量進行排序並確定有多少個不同的字串。結果作為最終計數返回。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int countDistinctStrings(string s, int n, int k) {
   vector<string> v;
   v.push_back(s);
   for(int i=0; i<k; i++) {
      int len = v.size();
      for(int j=0; j<len; j++) {
        string temp = v[j];
         for(int x=0; x<n; x++) {
            for(int y=x+1; y<n; y++) {
               if(temp[x] != temp[y]) {
                  swap(temp[x], temp[y]);
                  v.push_back(temp);
                  swap(temp[x], temp[y]);
              }
            }
         }
      }
   }
   sort(v.begin(), v.end());
   int cnt = unique(v.begin(), v.end()) - v.begin();
   return cnt;
} 
int main() {
   string s = "abb";
   int n = s.length();
   int k = 2;
   int ans = countDistinctStrings(s, n, k);
   cout << "Distinct strings after " << k << " operations: " << ans << endl;
   return 0;
}

輸出

Distinct strings after 2 operations: 3

方法 2:集合法

在此方法中,可以透過執行指定過程獲得的所有不同字串都可以儲存在一個集合中。如果生成的字串不存在,則可以將其新增到集合中。可以透過計算集合的大小來確定建立每個可能的字串後不同字串的數量。

語法

在此方法中,計算執行給定操作後不同的可能字串,語法通常包含以下步驟:

  • 初始化一個空集來儲存不同的字串。

distinct_strings = set ()
  • 透過將給定操作應用於原始字串來生成所有可能的字串。

def generate_strings(original_string, operations):
return new_strings
  • 將每個生成的字串新增到集合中以確保唯一性。

for string in generate_strings(original_string, operations):
   distinct_strings.add(string)
  • 最後,檢索不同字串的數量。

count = len(distinct_strings)

這種基於集合的解決方案利用了集合自動消除重複項的特殊能力,從而只儲存不同的字串。在將指定過程應用於原始字串後,您可以使用此方法有效地計算許多可能的字串。

演算法

步驟 1 - 建立一個名為“distinct_strings”的集合,用於儲存所有可獲得的不同字串。

步驟 2 - 將初始字串 S 包含在集合中。

步驟 3 - 對於列表中的每個 M 個操作 (L, R, X)

  • 對於集合中的每個唯一字串:

    • 複製字串。

    • 在複製的字串中,將索引 L 到 R(包含)處的字元更改為 X。

    • 將複製的字串新增到集合中。

步驟 4 - 提供“distinct_strings”集合的大小。

示例 2

為了儲存所有長度為 k 的不同子字串,我們使用此程式碼建立一個空的 distinctStrings 集合。我們透過迴圈遍歷給定的字串 s 來建立每個長度為 k 的可能子字串。然後使用每個子字串更新不同的 Strings 集合。集合的大小告訴我們,在所有子字串插入後,總共有多少個不同的潛在字串。這個值是函式的輸出,我們將其返回。

在前面的示例中,字串 s 是“abbabc”,目標是確定有多少個長度為 k=2 的唯一字串是可行的。程式返回的值為 3,表示可以透過將指定過程應用於字串 s 來建立 3 個長度為 2 的唯一字串。

#include<bits/stdc++.h>
using namespace std;

// Function to count the distinct possible strings
int countDistinctStrings(int n, int k, string s) {
   set<string> distinctStrings; // Create an empty set to store distinct strings

   // Create all possible sub strings of length k and insert them into the set
   for (int i = 0; i <= n-k; i++) {
      string temp = s.substr(i, k);
      distinctStrings.insert(temp);
   }

   // Calculate the total number of distinct strings
   int ans = distinctStrings.size();

   return ans;
}

// Driver code
int main() {
   int n = 6, k = 2; // Given values of n and k
      string s = "abbabc"; // Given string

   // Call the countDistinctStrings function and print the result
   int distinctStrings = countDistinctStrings(n, k, s);
   cout << "The number of distinct possible strings is: " << distinctStrings << endl;

   return 0;
}

輸出

The number of distinct possible strings is: 4

方法 3:動態規劃

此方法有效地利用動態規劃來計算唯一字串的數量。您可以定義一個狀態,該狀態可以表示在特定數量的操作後可以獲得的不同字串的數量。可以使用遞推關係根據先前狀態計算後續狀態的不同字串的數量。對於大型輸入,此方法可能更有效。

語法

C++ 中動態規劃方法的示例語法:

int countDistinctStrings(int n, vector& operations) {
  • 初始化表格或陣列

vector<int> dp(n + 1, 0);
  • 設定基本情況

dp[0] = 1;
  • 迭代子問題

for (int i = 1; i <= n; i++) { 
  • 根據先前的子問題計算值

for (int op : operations) {
   if (i >= op) {
      dp[i] += dp[i - op];
   }
}
}
  • 返回最終答案

   return dp[n];
}

演算法

以下是用動態規劃演算法計算執行給定操作後不同可能字串的數量:

步驟 1 - 初始化二維陣列 DP[n][k],其中 n 表示原始字串的長度,k 表示允許的運算元。DP[i][j] 的值表示可以使用原始字串的前 i 個字元和 j 個操作建立的不同字串的數量。

步驟 2 - 因為只能使用零個字元和零個操作建立一個字串,所以將 DP[0][0] 設定為 1。

步驟 3 - 程式應將 DP[i][j] 設定為 DP[x][j-1] 的累加和,其中 x 表示操作 j 中最後一個修改字元的索引,範圍從 0 到 i-1。如果沒有先前的操作,則 x 為零。

步驟 4 - 要計算 DP[i][j],請從 0 到 i-1 減去所有 DP[x][j-1] 的總和,其中 x 表示在第 j 次操作期間用相同的字元替換的最後一個字元。如果沒有先前的操作,則將 x 設定為 0。

步驟 5 - 返回 DP[n][k] 的值,該值表示透過將起始字串的所有 n 個字元與 k 個操作組合可以建立多少個唯一字串。

示例 1

一個 C++ 示例,說明如何使用動態規劃來計算執行指定操作後不同的潛在字串:

此示例中的 countDistinctStrings 方法有四個輸入引數:n、k、x 和 y。X 和 Y 是模值,而 n 表示字串的長度,k 表示可以包含在字串中的最大連續 1 的數量。

該函式在名為 dp 的二維陣列中儲存每個長度 i 處不同字串的數量和連續 1 的數量 j。使用 memset 方法將陣列設定為 0。

然後,根據之前的長度 i-1 和連續 1 的數量 j-1 更新 dp 陣列,該函式遍歷每個長度 i 和連續 1 的數量 j。如果連續 1 的數量等於 k,則使用模 y 代替模 x。

然後,該函式輸出 dp [n% 2][k],它是可以具有長度 n 並且最多有 k 個連續 1 的唯一字串的數量。

主函式使用 n、k、x 和 y 的指定值呼叫 countDistinctStrings 函式。然後將最終計數列印到控制檯。

#include <iostream>
#include <cstring>
using namespace std;

int countDistinctStrings(int n, int k, int x, int y) {
   int dp[2][k + 1];
   memset(dp, 0, sizeof(dp));
   dp[0][0] = 1;

   for (int i = 1; i <= n; i++) {
      int cur = i % 2;
      int prev = (i - 1) % 2;

      for (int j = 0; j <= k; j++) {
         dp[cur][j] = dp[prev][j];

         if (j > 0) {
            dp[cur][j] += dp[prev][j - 1];
            dp[cur][j] %= x;
         }   

         if (j == k) {
            dp[cur][j] -= dp[prev][j - 1];
            dp[cur][j] %= y;
            dp[cur][j] += y;
            dp[cur][j] %= y;
         }
      }
   }
   return dp[n % 2][k];
}

int main() {
   int n = 4;
   int k = 2;
   int x = 1000000007;
   int y = 998244353;

   int count = countDistinctStrings(n, k, x, y);

   cout << "Count of distinct possible strings: " << count << endl;

   return 0;
}

輸出

Count of distinct possible strings: 0

方法 4:組合方法

此方法可以使用組合數學直接計算不同的字串。例如,如果可以將“a”或“b”新增到字串的末尾,則可以使用“a”和“b”組合的公式來計算生成的唯一字串的數量。當處理最少的輸入量和過程計數時,此策略可能會有所幫助。

語法

完成上述操作後,組合方法計算不同潛在字串的語法如下:

  • 命名操作組:

operations are op1, op2,..., opk.
  • 指定限制:

restrictions = "c1, c2,..., cn"
  • 確定有多少個不同的字串:

count is equal to f(operations, constraints), 

其中 f 是一個組合函式,用於確定可以從給定的操作集和限制中生成多少個唯一字串。

根據操作和情況,可以使用不同的 f 函式實現。為了列舉潛在的字串,它通常涉及使用組合公式,例如排列、組合或生成函式。

演算法

步驟 1 - 將第一個字串“s”新增到字串集合 S 中。

步驟 2 - 對每個操作 (li, ri) 執行以下操作:

  • 透過翻轉子字串 s[li:ri] 中的字元,為 S 中的每個字串 t 建立一個新字串 u。

  • 將字串“u”包含在集合“S”中。

步驟 3 - 提供集合 S 的大小。

步驟 4 - 演算法完成。

該演算法的時間複雜度為 O(k * 2n),其中 n 是初始字串的長度,k 是操作次數。這是因為在最壞情況下,可能存在 2n 個不同的字串,每個字串都可能進行 k 次操作,從而產生總共 k * 2n 個字串。然而,實際中不同的字串數量可能更少,並且可以透過避免生成重複字串來改進演算法。

示例 4

本示例演示瞭如何使用 C++ 實現組合方法來計算應用操作後可能的字串數量。

在這個場景中,有一個長度為 n=5 的字串,可以使用 k=2 個過程生成唯一的字串。操作由一個整數向量表示,其中 1 表示在兩個相鄰的 1 之間插入一個字元,0 表示在兩個相鄰的 0 之間插入一個字元。我們將計算這些操作產生的唯一字串的數量。

count_possible_strings() 函式的輸入引數是字串長度 n、操作次數 k 和操作向量。然後,該函式使用二項式係數公式,在迴圈遍歷過程的過程中確定每次操作後可以形成的潛在字串的數量。所有操作完成後,它會累加可以生成的潛在字串數量並返回總計數。

一個名為 binomial_coefficient() 的輔助函式使用公式 n! / (k! * (n-k)!) 計算二項式係數 nCk。為了防止溢位,分子和分母使用迴圈分別計算。

在主程式碼中,我們使用輸入值呼叫 count_possible_strings() 方法,然後返回結果。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// Helper function to calculate the binomial coefficient nCk
int binomial_coefficient(int n, int k) {
   if (k > n - k) k = n - k;
   int res = 1;
   for (int i = 0; i < k; i++) {
      res *= (n - i);
      res /= (i + 1);
   }
    return res;
}
// Function to count the distinct possible strings after performing given operations
int count_possible_strings(int n, int k, vector<int>& operations) {
   int num_ones = 1, num_zeros = 0, res = 0;
   for (int i = 0; i < operations.size(); i++) {
      if (operations[i] == 1) {
         res += binomial_coefficient(num_ones + num_zeros, num_ones);
         num_ones++;
      } else {
         num_zeros++;
      }
   }
   res += binomial_coefficient(num_ones + num_zeros, num_ones);
   return res;
}
// Driver code
int main() {
   int n = 5, k = 2;
   vector<int> operations = {1, 0, 1};
   int count = count_possible_strings(n, k, operations);
   cout << "Count of distinct possible strings = " << count << endl;
   return 0;
}

輸出

Count of distinct possible strings = 8

結論

總之,確定特定操作集產生的不同潛在字串的數量是一個具有多種解決方案的挑戰性問題。可以使用排列和組合等數學概念以及動態規劃等演算法技術來快速計算給定操作形成的唯一字串的數量。但是,這些演算法的執行時間和記憶體需求可能會隨著問題規模和新增操作呈指數級增長。因此,在選擇哪種方法能夠在效率和準確性之間取得平衡時,務必考慮每個問題的例項。

更新於:2023年7月31日

677 次瀏覽

開啟你的職業生涯

完成課程獲得認證

開始
廣告