分解字符串以形成有效的表达式

时间:2018-06-09 02:43:31

标签: algorithm data-structures

我得到一个字符串 S (整数)和一个数字 N 。我想插入任意数量的' +'在 S 中,使得总和等于 N

Ex:<br>
S = 15112 and N = 28<br>
Ans is : 15+11+2<br>
S = 120012 and N = 33<br>
Ans is : 1+20+012<br>
S = 123 and N = 123<br>
Ans is : 123

给出:| S | &lt; = 120且N <= 10 ^ 6
保证给出 S N 使得总是可以形成有效的表达式。有没有什么算法可以解决这个问题?我试着考虑但不能提出解决方案。

3 个答案:

答案 0 :(得分:2)

可能有更有效的方法来做到这一点,但因为到目前为止你什么都没有......

您可以简单地找到布尔数组的所有组合,以指示数字之间是否应存在加号。

例如:输入112134时,1 + 12 + 13 + 4可以用布尔数组[true, false, true, false, true]表示,表示第1,第3和第5个数字后面有加号。然后问题减少到找到哪些组合添加到您的号码。有很多方法可以找到组合。递归回溯是一种经典。

在javascript / node中,这可能如下所示:

function splitOnIndexes(arr, a) {
  // split the array into numbers based on the booleans
  let current = "" + arr[0]
  let output = []
  for (let i = 0; i < a.length; i++) {
    if (!a[i]) {
      current += arr[i + 1]
    } else {
      output.push(current)
      current = "" + arr[i + 1]
    }
  }
  output.push(current)
  return output
}

function findSum(input, total) {
  function backtrack(n, k = 0, a = []) {
    const sum = (arr) => arr.reduce((a, c) => a + parseInt(c), 0)
    if (k === n) {
      let ans = splitOnIndexes(input, a)
      if (sum(ans) === total) {
        console.log(ans.join(' + '))
      }
    } else {
      k = k + 1
      let c = [true, false]
      for (let i = 0; i < 2; i++) {
        a[k - 1] = c[i]
        backtrack(n, k, a)
      }
    }
  }

  backtrack(input.length - 1)
}

findSum('15112', 28)

findSum('120012', 33)

findSum('123', 123)

如您所见,可能有多个答案。您的第一个示例已使用15+1+1215+11+2解决。如果你只需要一个,你当然可以提早停止。

答案 1 :(得分:1)

这个想法是使用动态编程,你只关心0到10 ^ 6之间的和,并且只有120个可能的索引。如果dp [i] [j] = x,则意味着从字符串的索引x开始,我们去了索引i(所以我们在i之前添加了一个+),我们得到了j的总和。这导致O(| S | * N)解:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

string s;
long n;

long dp[123][1000001];

void solve (int index, long sum) {//index = what index of s still remains to scan. sum = the sum we have accumulated till now
    if (sum >= n or index >= s.length()) return;
    if (dp[index][sum] != -1) return;
    if (index == n and sum == n) return;

    long num = 0;

    for (int i = 0; i < 7 && index + i < s.length(); i++) { //N has 6 digits at most
        num = stoi(s.substr(index, i + 1)); 
        solve(index + i + 1, sum + num); 
        if (sum + num <= n) {
            dp[index + i + 1][sum + num] = index;
        }
    }

}

int main () {
    cin >> s;
    cin >> n;

    for (int i = 0; i < 121; i++) {
        for (int j = 0; j < 1000001; j++) {
            dp[i][j] = -1;
        }
    }

    solve(0, 0);

    int sum = n;
    int idx = s.length();

    vector<string> nums;
    //reconstruct solution
    while (idx != 0) {
        nums.push_back(s.substr(dp[idx][sum], idx - dp[idx][sum])); 
        idx = dp[idx][sum];
        sum -= stoi(nums[nums.size() - 1]);
    }

    for (int i = nums.size() -1; i >= 0; i--) {
        cout << nums[i];
        if (i != 0) cout << "+";
    }

}

答案 2 :(得分:0)

这是一个Ruby版本,具有逐步解释的算法,因此您可以轻松地使用C ++编写代码(或者稍后再尝试)。

# Let's consider that we extracted the values from text, so we already have the string of int and the result as integer:

string_of_int = "15112"
result = 28

# The basic idea is to find a map (array) that tells how to group digits, for example

sum_map = [2, 1, 2]

# This means that string_of_int is mapped into the following numbers
# 15, 1, 12
# then sum the numbers, in this case 15+1+12 = 28

# For finding a the solution we need to map
# all the possible combinations of addition given the n digits of the string_of_int then check if the sum is equal to the result

# We call k the number of digits of string_of_int
# in ruby we can build an array called sum_maps
# containing all the possible permutations like this:

k = string_of_int.length # => 5
sum_maps = []
k.times do |length|
  (1..k).to_a.repeated_permutation(length).each {|e| sum_maps << e if e.inject(:+) == k}
end
sum_maps
# => [[1, 5], [2, 4], [3, 3], [4, 2], [5, 1], [1, 1, 4], [1, 2, 3], [1, 3, 2], [1, 4, 1], [2, 1, 3], [2, 2, 2], [2, 3, 1], [3, 1, 2], [3, 2, 1], [4, 1, 1]]

# Now must check which of of the sum_map is giving us the required result.
#
# First, to keep the code short and DRY,
# better to define a couple of useful methods for the String class to use then:

class String
  def group_digits_by(sum_map)
    string_of_int_splitted = self.split("")
    grouped_digits = []
    sum_map.each { |n| grouped_digits << string_of_int_splitted.shift(n).join.to_i}
    grouped_digits.reject { |element| element == 0 }
  end

  def sum_grouped_of_digits_by(sum_map)
    group_digits_by(sum_map).inject(:+)
  end
end

# So we can call the methods directly on the string
# for example, in ruby:

string_of_int.group_digits_by sum_map #=> [15, 1, 12]
string_of_int.sum_grouped_of_digits_by sum_map #=> 28

# Now that we have this metods, we just iterate through the sum_maps array
# and apply it for printing out the sm_map if the sum of grouped digits is equal to the result
# coded in ruby it is:

combinations = []
sum_maps.each { |sum_map| combinations << string_of_int.group_digits_by(sum_map) if string_of_int.sum_grouped_of_digits_by(sum_map) == result }
p combinations.uniq
# => [[15, 1, 12], [15, 11, 2]]

简而言之,作为Ruby模块编写,它变为:

module GuessAddition
  class ::String
    def group_digits_by(sum_map)
      string_of_int_splitted = self.split("")
      grouped_digits = []
      sum_map.each { |n| grouped_digits << string_of_int_splitted.shift(n).join.to_i}
      grouped_digits.reject { |element| element == 0 }
    end
    def sum_grouped_of_digits_by(sum_map)
      group_digits_by(sum_map).inject(:+)
    end
  end
  def self.guess_this(string_of_int, result)
    k = string_of_int.length
    sum_maps = []
    k.times { |length| (1..k).to_a.repeated_permutation(length).each {|e| sum_maps << e if e.inject(:+) == k} }
    combinations = []
    sum_maps.each { |sum_map| combinations << string_of_int.group_digits_by(sum_map) if string_of_int.sum_grouped_of_digits_by(sum_map) == result }
    combinations.uniq
  end
end

p GuessAddition::guess_this("15112", 28) # => [[15, 1, 12], [15, 11, 2]]