【备战秋招】每日一题:2023.05-B卷-华为OD机试 - 字符串化繁为简

为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1360

题目描述

给定一个输入字符串,字符串只可能由英文字母 (‘ a a a’ ~‘ z z z’、‘ A A A’ ~ ‘ Z Z Z’ )和左右小括号 (‘(’、‘)’)组成。

当字符里存在小括号时,小括号是成对的,可以有一个或多个小括号对,小括号对不会嵌套,小括号对内可以包含 1 1 1个或多个英文字母,也可以不包含英文字母。

当小括号对内包含多个英文字母时,这些字母之间是相互等效的关系,而且等效关系可以在不同的小括号对之间传递,即当存在“ a a a"和’ b b b’等效和存在’ b b b’和’ c c c’ 等效时,‘ a a a’ 和 ' c c c’也等效,另外,同一个英文字母的大写字母和小写字母也相互等效 (即使它们分布在不同的括号对里)

需要对这个输入字符串做简化,输出一个新的字符串,输出字符串里只需保留输入字符串里的没有被小括号对包含的字符(按照输入字符串里的字符顺序) ,并将每个字符替换为在小括号对里包含且字典序最小的等效字符。

如果简化后的字符串为空,请输出为" 0 0 0”。

示例:

输入字符串为" n e v e r ( d o n t ) g i v e ( r u n ) u p ( f ) ( ) never(dont)give(run)up(f)() never(dont)give(run)up(f)()",初始等效字符集合为(‘ d d d’,‘ o o o’,‘ n n n’,‘ t t t’)、(‘ r r r’,‘ u u u’,‘ n n n’),由于等效关系可以传递,因此最终等效字符集合为(‘ d d d’,‘ o o o’,‘ n n n’,‘ t t t’,‘ r r r’,‘ u u u’),将输入字符串里的剩余部分按字典序最小的等效字符替换后得到" d e v e d g i v e d p devedgivedp devedgivedp

输入描述

i n p u t input input_ s t r i n g string string

输入为 1 1 1行,代表输入字符串。

输出描述

o u t p u t output output_ s t r i n g string string

输出为 1 1 1行,代表输出字符串。

备注

输入字符串的长度在 1 1 1~ 100000 100000 100000之间。

样例

输入

()abd

输出

abd

说明

输入字符串里没有被小括号包含的子字符串为" a b d abd abd",其中每个字符没有等效字符,输出为" a b d abd abd"

输入

(abd)demand(fb)for

输出

aemanaaor

说明

等效字符集为(‘ a a a’,‘ b b b’,‘ d d d’,‘ f f f’),输入字符串里没有被小括号包含的子字符串集合为’ d e m a n d f o r demandfor demandfor”,将其中字符替换为字典序最小的等效字符后输出为:“ a e m a n a a o r aemanaaor aemanaaor

输入

()happy(xyz)new(wxy)year(t)

输出

happwnewwear

说明

等效字符集为(‘ x x x’,‘ y y y’,‘ z z z’,‘ w w w’),输入字符串里没有被小括号包含的子字符串集合为" h a p p y n e w y e a r happynewyear happynewyear”,将其中字符替换为字典序最小的等效字符后输出为:" h a p p w n e w w e a r happwnewwear happwnewwear

输入

()abcdefgaAC(a)(Ab)(C)

输出

AAcdefgAC

说明

等效字符集为(‘ a a a’,‘ A A A’,‘ b b b’),输入字符里没有被小括号包含的子字符串集合为" a b c d e f g A C abcdefgAC abcdefgAC",将其中字符替换为字典序最小的等效字符后输出为:“ A A c d e f g A C AAcdefgAC AAcdefgAC

思路

该题是一道模拟题。解决这题,我们发现难点在于如何对于处理各个字符的等价关系。假如我们知道了所有字母的等价关系,那么我们直接遍历一遍去除括号内元素的字符串,将每一个元素按照等价关系进行转换即可。

因此,题目关键就是等价关系。

首先,我们发现等价关系都是出现在括号内部,因此我们可以按照直觉将每一个括号内部的元素组成一个等价关系。而所用的数据结构,可以是 l i s t list list也可以是 s e t set set。这里我们选择 s e t set set。我们将每一组括号内部元素组合成一个 s e t set set之后,第二个问题就是如何合并。

这个问题的暴力解法很容易想到,那就是对每一个 s e t set set找所有其他 s e t set set是否和这个 s e t set set存在相同元素,如果存在,就合并并删除其中一个 s e t set set,不存在,就继续查找。

因此。问题的解决就出来了,我们首先需要将原字符串进行分离,将每一个括号内部的元素组合成一个 s e t set set,将括号外的元素作为待转换串。之后,我们通过暴力解法将所有 s e t set set合并。合并之后,我们对于待转换串进行转换,每次转化就去找目标字符所在的 s e t set set,并替换为字典序最小的那一个。

类似题目推荐

代码

C++

#include <iostream>
#include <vector>
#include <queue>
#include <sstream>

using namespace std;

int getResult(vector<vector<string>>& matrix) {
    int row = matrix.size();
    int col = matrix[0].size();

    // 记录宜居取坐标位置
    queue<pair<int, int>> que;
    // 记录可改造区数量
    int need = 0;

    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            if (matrix[i][j] == "YES") {
                que.push({i, j});
            } else if (matrix[i][j] == "NO") {
                need += 1;
            }
        }
    }

    // 如果没有宜居区,则无法改造,直接返回-1
    if (que.empty()) {
        return -1;
    }
    // 如果全是宜居区,则无需改造,直接返回0
    else if (que.size() == row * col) {
        return 0;
    }

    // 记录花费的天数
    int day = 0;
    // 上,下,左,右四个方向的偏移量
    vector<pair<int, int>> offsets = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

    // 图的多源BFS模板
    while (!que.empty() && need > 0) {
        queue<pair<int, int>> newQueue;

        while (!que.empty()) {
            pair<int, int> coordinates = que.front();
            que.pop();

            int x = coordinates.first;
            int y = coordinates.second;

            for (auto& offset : offsets) {
                // 上,下,左,右四个方向扩散
                int newX = x + offset.first;
                int newY = y + offset.second;

                // 如果新位置没有越界,且为NO,则可以被改造
                if (newX >= 0 && newX < row && newY >= 0 && newY < col && matrix[newX][newY] == "NO") {
                    matrix[newX][newY] = "YES";
                    newQueue.push({newX, newY});
                    need -= 1;
                }
            }
        }

        day += 1;
        que = newQueue;
    }

    if (need == 0) {
        return day;
    } else {
        return -1;
    }
}

int main() {
    vector<vector<string>> matrix;

    string line;
    while (getline(cin, line)) {
        istringstream iss(line);
        vector<string> row;
        string value;
        while (iss >> value) {
            row.push_back(value);
        }
        matrix.push_back(row);
    }

    cout << getResult(matrix) << endl;

    return 0;
}

python

def getResult(str):
    # 主体字符容器
    sb = []
    # 等效字符容器的集合
    eqs = []

    # isOpen标志,表示有没有遇到 '(' 字符
    isOpen = False
    s = list(str.strip())
    # 下面逻辑用于从输入字符串中解析处主体字符,和等效字符
    for c in s:
        if c == '(':
            isOpen = True
            eqs.append(set())
        elif c == ')':
            isOpen = False
            if len(eqs[-1]) == 0:
                eqs.pop()  # 如果等效字符容器为空,则删除
        else:
            if not isOpen:
                sb.append(c)
            else:
                eqs[-1].add(c)
#     return len(sb)
    # 暴力的对等效字符容器进行合并
    while True:
        for i in range(len(eqs)):
            for j in range(i + 1, len(eqs)):
                if canCombine(eqs[i], eqs[j]):
                    eqs[i].update(eqs[j])
                    eqs.pop(j)
                    break
            else:
                continue
            break
        else:
            break

    cArr = list(sb)
    
    # 替换主体字符容器中的字符
    for eq in eqs:
        ls = list(eq)
        ls.sort()
        t = ls[0]
        for i in range(len(cArr)):
            if cArr[i] in eq:
                cArr[i] = t
    
    ans = "".join(cArr)
    
    # 如果简化后的字符串为空,请输出为"0"。
    return "0" if len(ans) == 0 or ans == '' else ans


def canCombine(set1, set2):
    # c 是小写字符
    for c in range(ord('a'), ord('z') + 1):
        uc = chr(c - 32)  # uc是大写字符
        if (chr(c) in set1 or uc in set1) and (chr(c) in set2 or uc in set2):
            return True
    return False


s = input()
print(getResult(s))

Java

import java.util.LinkedList;
import java.util.Scanner;
import java.util.TreeSet;
 
public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    String s = sc.nextLine();
    System.out.println(getResult(s));
  }
 
  public static String getResult(String s) {
    // 主体字符容器
    StringBuilder sb = new StringBuilder();
    // 等效字符容器的集合
    LinkedList<TreeSet<Character>> eqs = new LinkedList<>();
 
    //  isOpen标志,表示有没有遇到 '(' 字符
    boolean isOpen = false;
 
    // 下面逻辑用于从输入字符串中解析处主体字符,和等效字符
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
 
      if (c == '(') {
        isOpen = true;
        eqs.add(new TreeSet<>());
      } else if (c == ')') {
        isOpen = false;
        if (eqs.getLast().size() == 0) eqs.removeLast(); // 如果等效字符容器为空,则删除
      } else {
        if (!isOpen) sb.append(c);
        else eqs.getLast().add(c);
      }
    }
 
    // 暴力的对等效字符容器进行合并
    outer:
    while (true) {
      for (int i = 0; i < eqs.size(); i++) {
        for (int j = i + 1; j < eqs.size(); j++) {
          if (canCombine(eqs.get(i), eqs.get(j))) {
            eqs.get(i).addAll(eqs.get(j));
            eqs.remove(j);
            continue outer;
          }
        }
      }
      break;
    }
 
    char[] cArr = sb.toString().toCharArray();
 
    // 替换主体字符容器中的字符
    for (TreeSet<Character> eq : eqs) {
      Character t = eq.first();
      for (int i = 0; i < cArr.length; i++) {
        if (eq.contains(cArr[i])) cArr[i] = t;
      }
    }
 
    String ans = new String(cArr);
 
    // 如果简化后的字符串为空,请输出为"0"。
    return ans.length() == 0 ? "0" : ans;
  }
 
  public static boolean canCombine(TreeSet<Character> set1, TreeSet<Character> set2) {
    // c 是小写字符
    for (char c = 'a'; c <= 'z'; c++) {
      char uc = (char) (c - 32); // uc是大写字符
      if ((set1.contains(c) || set1.contains(uc)) && (set2.contains(c) || set2.contains(uc))) {
        return true;
      }
    }
    return false;
  }
}

Go

package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strings"
)

func getResult(str string) string {
	var ans string
	var sb []byte
	var eqs []map[byte]bool
	isOpen := false
	s := strings.TrimSpace(str)

	for i := 0; i < len(s); i++ {
		c := s[i]
		if c == '(' {
			isOpen = true
			eqs = append(eqs, make(map[byte]bool))
		} else if c == ')' {
			isOpen = false
			if len(eqs[len(eqs)-1]) == 0 {
				eqs = eqs[:len(eqs)-1]
			}
		} else {
			if !isOpen {
				sb = append(sb, c)
			} else {
				eqs[len(eqs)-1][c] = true
			}
		}
	}

	for {
		merged := false
		for i := 0; i < len(eqs); i++ {
			for j := i + 1; j < len(eqs); j++ {
				if canCombine(eqs[i], eqs[j]) {
					for k := range eqs[j] {
						eqs[i][k] = true
					}
					eqs = append(eqs[:j], eqs[j+1:]...)
					merged = true
					break
				}
			}
			if merged {
				break
			}
		}
		if !merged {
			break
		}
	}

	cArr := []byte(sb)

	for _, eq := range eqs {
		ls := make([]byte, 0, len(eq))
		for k := range eq {
			ls = append(ls, k)
		}
		sort.Slice(ls, func(i, j int) bool {
			return ls[i] < ls[j]
		})
		t := ls[0]
		for i := 0; i < len(cArr); i++ {
			if _, ok := eq[cArr[i]]; ok {
				cArr[i] = t
			}
		}
	}

	ans = string(cArr)

	if ans == "" {
		return "0"
	}
	return ans
}

func canCombine(set1, set2 map[byte]bool) bool {
	for c := byte('a'); c <= 'z'; c++ {
		uc := c - 32
		if (set1[c] || set1[uc]) && (set2[c] || set2[uc]) {
			return true
		}
	}
	return false
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	str, _ := reader.ReadString('\n')
	fmt.Println(getResult(str))
}

Js

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");
 
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
 
rl.on("line", (line) => {
  console.log(getResult(line));
});
 
function getResult(s) {
  // 主体字符容器
  const cArr = [];
  // 等效字符容器的集合
  const eqs = [];
 
  //  isOpen标志,表示有没有遇到 '(' 字符
  let isOpen = false;
 
  // 下面逻辑用于从输入字符串中解析处主体字符,和等效字符
  for (let i = 0; i < s.length; i++) {
    const c = s[i];
 
    if (c == "(") {
      isOpen = true;
      eqs.push(new Set());
    } else if (c == ")") {
      isOpen = false;
      if (eqs.at(-1).size == 0) eqs.pop(); // 如果等效字符容器为空,则删除
    } else {
      if (!isOpen) cArr.push(c);
      else eqs.at(-1).add(c);
    }
  }
 
  // 暴力的对等效字符容器进行合并
  outer: while (true) {
    for (let i = 0; i < eqs.length; i++) {
      for (let j = i + 1; j < eqs.length; j++) {
        if (canCombine(eqs[i], eqs[j])) {
          eqs[i] = new Set([...eqs[i], ...eqs[j]]);
          eqs.splice(j, 1);
          continue outer;
        }
      }
    }
    break;
  }
 
  // 替换主体字符容器中的字符
  for (let eq of eqs) {
    const t = [...eq].sort()[0];
    for (let i = 0; i < cArr.length; i++) {
      if (eq.has(cArr[i])) cArr[i] = t;
    }
  }
 
  const ans = cArr.join("");
 
  // 如果简化后的字符串为空,请输出为"0"。
  return ans.length == 0 ? "0" : ans;
}
 
function canCombine(set1, set2) {
  for (let c = 97; c <= 122; c++) {
    const lower = String.fromCharCode(c); // 小写字符
    const upper = String.fromCharCode(c - 32); // 大写字符
    if (
      (set1.has(lower) || set1.has(upper)) &&
      (set2.has(lower) || set2.has(upper))
    ) {
      return true;
    }
  }
 
  return false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值