目标字符串动态交叉拼接
题意:
判断目标字符串是不是可以由其他两个字符串动态交叉拼接
示例:
字符串a: aabcc 字符串b: dbbca 目标字符串:aadbbcbcac
目标字符串可以由 字符串a和字符串b交叉拼接
解题思路:
dp[i][j] 表示字符串的前i+j 可以由s1的前i个字符 和s2的前j个字符组成
表达式:
dp[i][j] = dp[i-1][j]&& s1[i-1]=target[i+j-1] || dp[i][j-1] && s2[j-1]=targeti+j-1
表示 前i-1+j个字符可由s1前i-1个字符和s2前j个字符组成,如果目标字符第i+j个字符与s1的第i个字符相等,那么 前i+j个字符可以由s1的前i个字符和s2的前j个字符组成
或者:
前i-1+j个字符可由s1前i个字符和s2前j-1个字符组成,如果目标字符第i+j个字符与s2的第j个字符相等,那么 前i+j个字符可以由s1的前i个字符和s2的前j个字符组成
代码:
static boolean sIntervalSubStr(String s1, String s2, String target) {
if (s1.length() + s2.length() != target.length()) return false;
int len1 = s1.length();
int len2 = s2.length();
int len3 = target.length();
char[] ch1 = s1.toCharArray();
char[] ch2 = s2.toCharArray();
char[] ch3 = target.toCharArray();
//dp表示记录是否可以组成
boolean [][] dp = new boolean[len1+1][len2+1];
//表示都为空的时候肯定可以
dp[0][0]=true;
for (int i=1;i<ch1.length;i++){
dp[i][0]= dp[i-1][0]&& ch1[i-1]==ch3[i-1];
}
for (int i=1;i<ch2.length;i++){
dp[0][i]= dp[0][i-1]&& ch2[i-1]==ch3[i-1];
}
for (int i =1;i<=len1;i++){
for (int j=1;j<=len2;j++){
dp[i][j] = (dp[i-1][j]&& (ch1[i-1]==ch3[i+j-1])) || (dp[i][j-1] && (ch2[j-1]==ch3[i+j-1]));
}
}
return dp[len1][len2];
}
public static void main(String[] args) {
System.out.println(sIntervalSubStr("aabcc", "dbbca","aadbbcbcac"));
}
字符串编辑距离
题意:
字符串的编辑距离表示 一个字符串可以由另外一个字符串 通过最少的编辑次数 变成另外一个字符, 编辑操作包括 将一个字符进行替换 插入一个字符 删除一个字符
示例:
字符串s1=‘a’ 字符串s2=‘b’ 编辑距离为1 即 a替换为b
字符串s1=‘aab’ 字符串s2=‘db’ 编辑距离为2 即 s1删除第一个字符a 将第二个字符a替换为d
解题思路
使用二维数组dp记录最小替换次数 , dp[i][j]表示 字符串s1的前i个字符串转换为s2字符串的前j个字符串的最小编辑距离
when s1[i]==s2[j] 表示s1字符串的第i位置的字符等于s2字符串第j位置的字符, then dp[i][j]= dp[i-1][j-1]
when s1[i]!=s2[j] 表示s1字符串的第i位置的字符不等于s2字符串第j位置的字符, then dp[i][j] = Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
s1[i]!=s2[j] 只能进行 删除s1[i] , 替换s1[i]为s2[j], 插入一个s2[j]字符
意思是:
要么s1字符串第i个位置插入s2[j]位置对应的字符 dp[i][j] = dp[i-1][j]+1
要么s2字符串第j个位置插入s1[i]位置对应的字符 dp[i][j] = dp[i][j-1]+1
要么s1字符串第i位置字符被s2 字符串第j位置字符替换 dp[i][j] = dp[i-1][j-1]+1
代码:
static int editDistance(String s1, String s2) {
int len1 = s1.length();
int len2 = s2.length();
//dp[i][j]表示 第一个字符串前i个字符变成 目标字符的前j个字符需要编辑的次数
int [][] dp = new int[len1+1][len2+1];
for (int i=1;i<=len1;i++){
//表示特殊情况如s2为空串 那么s1转换为s2字符串 字符编辑为删除i个s1字符 编辑距离为i
dp[i][0]=i;
}
for (int j=1;j<=len2;j++){
//表示特殊情况如s1为空串 那么s1转换为s2字符串 字符编辑为插入i个s2字符 编辑距离为i
dp[0][j]=j;
}
for (int i=1;i<=len1;i++){
for (int j=1;j<=len2;j++){
if(s1.charAt(i-1)==s2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j] = Math.min(Math.min(dp[i-1][j],dp[i][j-1]), dp[i-1][j-1])+1;
}
}
}
return dp[len1][len2];
}
public static void main(String[] args) {
System.out.println(editDistance("a", "d"));
System.out.println(editDistance("aa", "db"));
System.out.println(editDistance("aab", "db"));
System.out.println(editDistance("aabcc", "dbbca"));
}
最长回文子串
题意:
一个字符串的包含的最长回文串 ,如aba, aabbaa这些都是回文串
解题思路
判断一个字符串的最大回文串,使用二维数组dp记录字符串i到j位置的字符是不是回文串,首先二维数字对角线肯定是回文,通过循环遍历
如果字符串s的最左端i和最右端J字符相等 且字符串s的i-1到j-1的子字符串也是回文,那么字符串s是回文串,特殊情况 字符串s的长度不大于2且左右两端字符相等,那么该字符串也是回文如aa
示例
abcbabc 字符串的最大回文串是 cbabc
代码
public class LongerstSubStr {
/**
* 动态规划
* l =r dp[l][r] =true
* dp[l][r] = true 满足 dp[l+1][r-1]=true 且s[l]=s[r]
*/
public String longSubStr(String s) {
if (s == null || s.length() < 2) {
return s;
}
int strLen = s.length();
int maxStart = 0;
int maxEnd = 1;
int maxLen = 1;
boolean[][] dp = new boolean[strLen][strLen];
for (int i = 0; i < strLen; i++) {
dp[i][i] = true;
}
for (int r = 1; r < strLen; r++) {
for (int l = 0; l < r; l++) {
if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
dp[l][r] = true;
if (r - l + 1 > maxLen) {
maxLen = r - l + 1;
maxStart = l;
maxEnd = r;
}
}
}
}
return s.substring(maxStart, maxEnd + 1);
}
public static void main(String[] args) {
LongerstSubStr subStr = new LongerstSubStr();
String s = subStr.longSubStr("abcbabc");
System.out.printf(s);
}
}
无重复字符的最长字符串
题目描述
字符串s中不包含重复字符的最大子字符串
解题思路
利用滑动窗口的形式 记录不重复字符串的起始位置,已经最长长度
示例
如字符串abdead 的最长字符串 可以是 abde 也可以是bdea 这个根据题意是第一个还是最后一个最长字符串
代码
public static int maxLengthNoRepeatStrLenth(String s) {
Map<Character, Integer> map = new HashMap<>();
//最长长度
int maxLen = 1;
int end = 0,start=0;
for (; end < s.length(); end++) {
if (map.containsKey(s.charAt(end))) {
start = Math.max(map.get(s.charAt(end)),start);
}
map.put(s.charAt(end), end + 1);
maxLen = Math.max(maxLen, end - start + 1);
}
System.out.println(s.substring(start,start+maxLen));
return maxLen;
}
public static void main(String[] args) {
System.out.println(maxLengthNoRepeatStrLenth("aabcaabc"));
}
字符串相加
题意:
给出两个数字字符串,求两个字符串的相加之和并输出,要求不可使用BigDecimal大数计算
代码:
public static String addString(String s1, String s2) {
int i = s1.length() - 1, j = s2.length() - 1, add = 0;
StringBuilder builder = new StringBuilder();
while (i >= 0 || j >= 0 || add != 0) {
int x = i >= 0 ? s1.charAt(i)-'0' : 0;
int y = j >= 0 ? s2.charAt(j) -'0': 0;
int sum = x + y + add;
builder.append(sum % 10);
add = sum / 10;
i--;
j--;
}
return builder.reverse().toString();
}
public static void main(String[] args) {
System.out.println(addString("12345","1234567"));
System.out.println(new BigDecimal("12345").add(new BigDecimal("1234567")).toString());
}
字符串相乘
题意:
给出两个数字字符串,求字符串相乘结果,要求不可以使用 BigDecimal
代码:
public static String multiplyString(String s1, String s2) {
if(s1.equals("0") || s1.equals("0")){
return "0";
}
int m = s1.length() , n = s2.length() ;
int [] ans = new int[m+n];
for (int i =m-1;i>=0;i-- ){
int x= s1.charAt(i)-'0';
for (int j=n-1;j>=0;j--){
int y= s2.charAt(j)-'0';
ans[i+j+1]+= x*y;
}
}
for (int i = m+n-1;i>0;i--){
ans[i-1]=ans[i]/10+ans[i-1];
ans[i]= ans[i]%10;
}
int index =ans[0]==0?1:0;
StringBuilder builder = new StringBuilder();
while (index<m+n){
builder.append(ans[index]);
index++;
}
return builder.toString();
}
public static void main(String[] args) {
System.out.println(multiplyString("12345","1234567"));
System.out.println(new BigDecimal("12345").multiply(new BigDecimal("1234567")).toString());
}
两个字符串最长子序列长度
题意:
两个字符串s1,s2 ,求字符串s1和字符串s2之间的最大子序列;如s1=“acdef”,s2=“abdefg” 那么"adef"既是s1的子序列也是s2的子序列,所有两者的最大子序列就是"adef"
代码
/**
* 最长公共子序列
* dp二维数组 很明显 dp[0][j] = 0 dp[i][0]=0;
* 如果si==sj 那么dp[i][j] = dp[i-1][j-1] +1 dp[i-1][j-1]表示s1前i-1个字符和s2前j-1个字符的最长公共子序列的长度
* 如果si!=sj 那么 dp[i][j] = dp[i][j-],dp[i-1][j]最大值 表示 s1 前i个字符和s2前j-1字符最大公共子序列长度
* 或者 s1前i-1个字符 s2前j字符 的最大子序列长度
* @param s1
* @param s2
* @return
*/
public static int LongerCommonSubStr(String s1, String s2) {
int m =s1.length();
int n= s2.length();
int[][] dp = new int[m+1][n+1];
for (int i=1;i<=m;i++){
for (int j=1;j<=n;j++){
if(s1.charAt(i-1) == s2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]= Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[m][n];
}
public static void main(String[] args) {
System.out.println(LongerCommonSubStr("abce","abcde"));
}
单词拆分
题意:
单词拆分 给出一个字符串s 和字符串列表 wordDict 作为字典 判断字符串s是可以使用wordDict字典组成,如字符串helloworld可以由字符串数组{“hello”,“world”}组成
代码:
public static boolean wordBreak(String s, List<String> wordDict) {
Set<String> wordDictSet = new HashSet(wordDict);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
//dp[i] 表示前i个字符可以由字符数组组成
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
public static void main(String[] args) {
String word ="hello world";
List<String> wordDict=new ArrayList<>(Arrays.asList("hello","world"));
System.out.println(wordBreak(word,wordDict));
}
尽可能使字符串相等
题意:
尽可能使字符串相等 给定两个长度相等的字符串 s t 其中 si 转换为ti 的开销记作 |si - ti| maxCost 表示最大允许的开销, 在开销小于maxCost情况下, 要求两个字符串尽可能相等
代码:
双指针
public static int equalsSubstring(String s, String t, int maxCost) {
int n = s.length();
int[] diff = new int[n];
for (int i = 0; i < n; i++) {
diff[i] = Math.abs(s.charAt(i) - t.charAt(i));
}
//start end 记录双指针的位置
int max = 0;
int start = 0, end = 0;
//记录 替换前start-end 个字符串的开销
int sum = 0;
while (end < n) {
sum += diff[end];
while (sum > maxCost) {
//开销大于最大开销 左指针就需要迁移 表示后面不可以在替换了
sum -= diff[start];
start++;
}
max = Math.max(max, end - start + 1);
//右指针继续往后移 知道开销代价大于maxCOst
end++;
}
return max;
}
public static void main(String[] args) {
String s = "adbcd";
String t = "adzef";
int maxCost = 10;
System.out.println(equalsSubstring(s,t,maxCost));
}