首页 > 试题广场 >

旋变字符串

[编程题]旋变字符串
  • 热度指数:605 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
一个字符串可以分解为多种二叉树。如果str长度为1,认为不可分解;如果str长度为N(N>1),左半部分长度可以为1~N-1,右半部分为剩下的长度,然后你可以交换左右两部分。并且左右部分可以按照同样的逻辑,继续分解。每一个形成的字符串都可以是原字符串的旋变字符串。现在给你两个字符串str1和str2,判断str2是否为str1的旋变字符串。

输入描述:
输入包括两行,第一行一个字符串,代表str1,第二行一个字符串,代表str2


输出描述:
如果str2是str1的旋变字符串请输出“YES”,否则输出“NO”。
示例1

输入

abcd
dbac

输出

YES

说明

abcd->d...abc->d...ab...c->d...b...a...c
示例2

输入

IJz
JzI

输出

YES

说明

左边为l右边为Jz交换  变Jzl

备注:
时间复杂度,额外空间复杂度
这是个范围上尝试的模型,记字符串的总长度为n。如果两个字符串都只有一个字符,那么只要它们两相等,那这两个字符就互为旋变串。如果两个字符串不止一个字符,而是有size个字符(size>1),将字符串分为左右两部分之后,就有如下两种可能性:
  1. 将假设str1左边的字符串长度为leftPart,则右边的长度为size-leftPart。如果str1[0:leftPart-1]str2[0:leftPart-1]互为旋变串,且str1[leftPart:size-1]str2[leftPart:size-1]互为旋变串,则整体互为旋变串。
  2. 如果str1[0:leftPart-1]str2[size-leftPart:size-1]互为旋变串,且str1[leftPart:size]str2[0:size-leftPart]互为旋变串,则整体互为旋变串。
整体有三个可变参数,因此本质上是个三维的动态规划问题:(1) 考察的字符串长度为size,取值范围是1到n;(2) str1可以从L1位置开始;(3) str2可以从L2位置开始。由于受到size的限制,(2)和(3)的取值范围均为0到n-size。首先,我们可以写出会超时的暴力递归尝试版本:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.HashMap;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        char[] str1 = br.readLine().toCharArray();
        char[] str2 = br.readLine().toCharArray();
        int n = str1.length;
        if(!isSameAlpha(str1, str2)){      // 检查一下两个字符串是否有相同的字符
            System.out.println("No");
        }else{
            System.out.println(recurrent(str1, str2, 0, 0, n)? "YES": "NO");
        }
    }
    
    private static boolean recurrent(char[] str1, char[] str2, int L1, int L2, int size) {
        // base case字符串长度为1的时候直接判断两个串是否相等就行
        if(size == 1){
            return str1[L1] == str2[L2];
        }
        for(int leftPart = 1; leftPart < size; leftPart++){
            // 尝试左边字符的数量
            if((recurrent(str1, str2, L1, L2, leftPart) && 
                recurrent(str1, str2, L1 + leftPart, L2 + leftPart, size - leftPart)) 
               || (recurrent(str1, str2, L1, L2 + size - leftPart, leftPart) && 
                   recurrent(str1, str2, L1 + leftPart, L2, size - leftPart))){
                return true;
            }
        }
        return false;
    }
    
    private static boolean isSameAlpha(char[] str1, char[] str2) {
        if(str1.length != str2.length) return false;
        HashMap<Character, Integer> map1 = new HashMap<>(), map2 = new HashMap<>();
        for(int i = 0; i < str1.length; i++){
            map1.put(str1[i], map1.getOrDefault(str1[i], 0) + 1);
            map2.put(str2[i], map2.getOrDefault(str2[i], 0) + 1);
        }
        for(char c: map1.keySet()){
            if(!map2.containsKey(c) || map1.get(c) != map2.get(c))
                return false;
        }
        return true;
    }
}
写出递归版本后,改成动态规划的思路就更加清晰,不容易出错
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.HashMap;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        char[] str1 = br.readLine().toCharArray();
        char[] str2 = br.readLine().toCharArray();
        int n = str1.length;
        if(!isSameAlpha(str1, str2)){      // 检查一下两个字符串是否有相同的字符
            System.out.println("NO");
        }else{
            System.out.println(dynamicProgramming(str1, str2)? "YES": "NO");
        }
    }
    
    private static boolean dynamicProgramming(char[] str1, char[] str2) {
        int n = str1.length;
        boolean[][][] dp = new boolean[n][n][n + 1];
        // base case考虑的串长度为1时,只要字符相等就是旋变串
        for(int l1 = 0; l1 < n; l1++){
            for(int l2 = 0; l2 < n; l2++){
                dp[l1][l2][1] = str1[l1] == str2[l2];
            }
        }
        for(int size = 2; size <= n; size++){
            for(int l1 = 0; l1 <= n - size; l1++){
                for(int l2 = 0; l2 <= n - size; l2++){
                    for(int leftPart = 1; leftPart < size; leftPart++){
                        if((dp[l1][l2][leftPart] && dp[l1 + leftPart][l2 + leftPart][size - leftPart]) || 
                           (dp[l1][l2 + size - leftPart][leftPart] && dp[l1 + leftPart][l2][size - leftPart])){
                            dp[l1][l2][size] = true;      // 只要有一个leftPart满足旋变串条件就可以break出去
                            break;
                        }
                    }
                }
            }
        }
        return dp[0][0][n];
    }
    
    private static boolean isSameAlpha(char[] str1, char[] str2) {
        if(str1.length != str2.length) return false;
        HashMap<Character, Integer> map1 = new HashMap<>(), map2 = new HashMap<>();
        for(int i = 0; i < str1.length; i++){
            map1.put(str1[i], map1.getOrDefault(str1[i], 0) + 1);
            map2.put(str2[i], map2.getOrDefault(str2[i], 0) + 1);
        }
        for(char c: map1.keySet()){
            if(!map2.containsKey(c) || map1.get(c) != map2.get(c))
                return false;
        }
        return true;
    }
}

编辑于 2021-12-08 22:55:17 回复(0)
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

#define MAXLEN 101

bool is_valid(char *str1, char *str2) {
    int char_map[256];
    memset(char_map, 0, sizeof(int) * 256);
    for (int i = 0; i < strlen(str1); i++) {
        char_map[str1[i]]++;
    }
    for (int i = 0; i < strlen(str2); i++) {
        if (--char_map[str2[i]] < 0) {
            return false;
        }
    }
    return true;
}

bool dp[MAXLEN][MAXLEN][MAXLEN];

int main(void) {
    char str1[MAXLEN], str2[MAXLEN];
    scanf("%s%s", str1, str2);
    if (!is_valid(str1, str2)) {
        printf("%s\n", "NO");
        return 0;
    }
    int len1 = (int) strlen(str1);
    int len2 = (int) strlen(str2);
    for (int i = 0; i < len1; i++) {
        for (int j = 0; j < len2; j++) {
            dp[i][j][1] = str1[i] == str2[j];
        }
    }
    for (int s = 2; s <= len1; s++) {
        for (int i = 0; i < len1; i++) {
            for (int j = 0; j < len2; j++) {
                for (int ls = 1; ls < s; ls++) {
                    if ((dp[i][j][ls] && dp[i + ls][j + ls][s - ls])
                        || (dp[i][j + s - ls][ls] && dp[i + ls][j][s - ls])) {
                        dp[i][j][s] = true;
                        break;
                    }
                }
            }
        }
    }
    printf("%s\n", dp[0][0][len1] ? "YES" : "NO");
    return 0;
}

发表于 2022-02-14 23:08:21 回复(0)
这个题目的测试用例也太弱了吧,java排名前面的方法,连样例1都过不去,居然能ac
发表于 2022-06-20 11:06:02 回复(0)

Rust

fn is_scramble(s1: String, s2: String) -> bool {
    if s1 == s2 {
        return true;
    }
    let s1 = s1.trim().chars().collect::<Vec<_>>();
    let s2 = s2.trim().chars().collect::<Vec<_>>();
    let mut s3 = s1.clone();
    let mut s4 = s2.clone();
    s3.sort_unstable();
    s4.sort_unstable();
    if s3 != s4 {
        return false;
    }
    let n = s1.len();
    let mut dp = vec![vec![vec![None; n + 1]; n]; n];
    process(&s1, &s2, 0, 0, n, &mut dp)
}

fn process(
    s1: &[char],
    s2: &[char],
    left1: usize,
    left2: usize,
    size: usize,
    dp: &mut Vec<Vec<Vec<Option<bool>>>>,
) -> bool {
    if let Some(item) = dp[left1][left2][size] {
        return item;
    }
    let mut res = false;
    if size == 1 {
        res = s1[left1] == s2[left2];
    } else {
        for i in 1..size {
            if (process(s1, s2, left1, left2, i, dp)
                && process(s1, s2, left1 + i, left2 + i, size - i, dp))
                || (process(s1, s2, left1, left2 + size - i, i, dp)
                && process(s1, s2, left1 + i, left2, size - i, dp))
            {
                res = true;
                break;
            }
        }
    }
    dp[left1][left2][size] = Some(res);
    res
}

fn main() {
    let mut s1 = String::new();
    let mut s2 = String::new();
    let stdin = std::io::stdin();
    let _ = stdin.read_line(&mut s1);
    let _ = stdin.read_line(&mut s2);
    let res = is_scramble(s1.trim().to_string(), s2.trim().to_string());
    println!("{}", if res {"YES"} else {"NO"});
}
发表于 2021-06-28 18:25:15 回复(0)
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
map<char,int> m;
int dp[110][110][110];
bool issameele(char str1[],char str2[]){
    int len1=strlen(str1);
    int len2=strlen(str2);
    if(len1!=len2)
        return false;
    for(int i=0;i<len1;++i){
        m[str1[i]]++;
    }
    for(int i=0;i<len2;++i){
        if(--m[str2[i]]<0)
            return false;
    }
    return true;
}
int main(){
   char str1[110],str2[110];
   while(scanf("%s %s",str1,str2)!=EOF){
       m.clear();
       int len=strlen(str1);
       if(!issameele(str1,str2))
       {
           printf("NO\n");
           continue;
       }
       memset(dp,sizeof(dp),0);
       for(int l=1;l<=len;++l){
           for(int i=0;i<=len-l;++i){
               for(int j=0;j<=len-l;++j){
                     if(l==1)
                     {
                         dp[i][j][l]=(str1[i]==str2[j]?1:0);
                         continue;
                     }
                     for(int k=1;k<l;++k){
                         if(dp[i][j][k]&&dp[i+k][j+k][l-k]||dp[i][j+l-k][k]&&dp[i+k][j][l-k])
                         {
                             dp[i][j][l]=1;
                             break;
                         }
                     }
               }
           }
       }
       if(dp[0][0][len]==1)
          printf("YES\n");
       else
          printf("NO\n");
   }
   return 0;
}


#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
map<char,int> m;
bool issameele(char str1[],char str2[]){
    int len1=strlen(str1);
    int len2=strlen(str2);
    if(len1!=len2)
        return false;
    for(int i=0;i<len1;++i){
        m[str1[i]]++;
    }
    for(int i=0;i<len2;++i){
        if(--m[str2[i]]<0)
            return false;
    }
    return true;
}
bool dfs(char str1[],char str2[],int l1,int l2,int len){
    if(len==1)
        return str1[l1]==str2[l2];
    for(int i=1;i<len;++i){
        if(dfs(str1,str2,l1,l2,i)&&dfs(str1,str2,l1+i,l2+i,len-i)||dfs(str1,str2,l1,l2+len-i,i)&&dfs(str1,str2,l1+i,l2,len-i))
             return true;
    }
    return false;
}
int main(){
   char str1[110],str2[110];
   while(scanf("%s %s",str1,str2)!=EOF){
       m.clear();
       int len=strlen(str1);
       if(!issameele(str1,str2))
       {
           printf("NO");
           continue;
       }
       if(dfs(str1,str2,0,0,len))
           printf("YES\n");
       else
           printf("NO\n");
   }
}

编辑于 2020-05-01 15:02:10 回复(0)
def mStr(str, m, n):
    s_lst=list(str)
    i=0
    while i<n:
        s_lst.insert(m, s_lst[i])
        i+=1
        m+=1
    return "".join(s_lst[n:m+n])
def res(str1, str2):
    m=len(str1)
    i=0
    s_lst=list()
    while i<m:
        if str1[i]==str2[0]:
            s_lst.insert(0,i)
        i+=1
    r=0
    for n in s_lst:
        str3=mStr(str1, m, n)
        if str3==str2:
            r=1
            break
    if r==1:
        print("YES")
    else:
        print("NO")
str1=input()
str2=input()
res(str1, str2)
发表于 2019-09-30 22:57:19 回复(0)
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string str1,str2;
    cin >> str1;
    cin >> str2;
    if(str1.size() != str2.size())
    {
        cout << "NO" <<endl;
    }else{
        int i = 0,k = str1.size()-1;
        while(i < k)
        {
            if(str1[i++] == str2[k--])
            {
                continue;
            }
            else{
                cout << "NO" <<endl;
                return 0;
            }
        }
    }
    cout << "YES" <<endl;
    return 0;
}

发表于 2019-09-22 11:42:20 回复(1)

问题信息

上传者:小小
难度:
7条回答 4394浏览

热门推荐

通过挑战的用户

查看代码