目次
1. N字変形(6)
指定された文字列を、 s
指定された行数に従って numRows
上から下、左から右の順にジグザグに配置します。
たとえば、入力文字列が 行"PAYPALISHIRING"
数 の場合、次のようになります。3
パーン・ アプリシグ ・イル
その後、出力を左から右に 1 行ずつ読み取って、次のような新しい文字列を生成する必要があります"PAHNAPLSIIGYIR"
。
文字列を指定した行数に変換するには、この関数を実装してください。
文字列変換(文字列 s, int numRows);
例 1:
入力: s = "PAYPALISHIRING"、numRows = 3 出力: "PAHNAPLSIIGYIR"
例 2:
入力: s = "PAYPALISHIRING"、numRows = 4 出力: "PINALSIGYAHRPI" 説明: PIN ALSIG YAHR PI
例 3:
入力: s = "A"、numRows = 1 出力: "A"
解決策 1 (Lituoの公式分析)
class Solution {
public String convert(String s, int numRows) {
int n = s.length(), r = numRows;
if (r == 1 || r >= n) {
return s;
}
int t = r * 2 - 2;
int c = (n + t - 1) / t * (r - 1);
char[][] mat = new char[r][c];
for (int i = 0, x = 0, y = 0; i < n; ++i) {
mat[x][y] = s.charAt(i);
if (i % t < r - 1) {
++x; // 向下移动
} else {
--x;
++y; // 向右上移动
}
}
StringBuffer ans = new StringBuffer();
for (char[] row : mat) {
for (char ch : row) {
if (ch != 0) {
ans.append(ch);
}
}
}
return ans.toString();
}
}
解決策 2 (公式解決策)
方法 1 の行列には未使用のスペースがたくさんありますが、最適化できますか?
文字が行列の行に追加されるたびに、その行の文字の右側に追加され、最終的な回答の構成には各行の null 以外の文字のみが使用されることに注意してください。したがって、行列の各行を空のリストとして初期化し、文字が行に追加されるたびに、その行のリストの最後に追加されます。
class Solution {
public String convert(String s, int numRows) {
int n = s.length(), r = numRows;
if (r == 1 || r >= n) {
return s;
}
StringBuffer[] mat = new StringBuffer[r];
for (int i = 0; i < r; ++i) {
mat[i] = new StringBuffer();
}
for (int i = 0, x = 0, t = r * 2 - 2; i < n; ++i) {
mat[x].append(s.charAt(i));
if (i % t < r - 1) {
++x;
} else {
--x;
}
}
StringBuffer ans = new StringBuffer();
for (StringBuffer row : mat) {
ans.append(row);
}
return ans.toString();
}
}
問題解決策 3 (電源ボタン)
これは K 神の解決策です。読んだ後は、素直に感心するとしか言えません。文字バッファを使用すると、この問題は完全に解決されました。本当に少し惰性で考えてしまって、最初は 2 次元配列しか思いつきませんでした時間。文字バッファーを使用することは予期していませんでした。そして国旗がとても上手に使われています。
class Solution {
public String convert(String s, int numRows) {
if(numRows < 2) return s;
List<StringBuilder> rows = new ArrayList<StringBuilder>();
for(int i = 0; i < numRows; i++) rows.add(new StringBuilder());
int i = 0, flag = -1;
for(char c : s.toCharArray()) {
rows.get(i).append(c);
if(i == 0 || i == numRows -1) flag = - flag;
i += flag;
}
StringBuilder res = new StringBuilder();
for(StringBuilder row : rows) res.append(row);
return res.toString();
}
}
2. 整数反転 (7)
32 ビット符号付き整数を指定すると x
、 x
その数値部分を反転した結果を返します。
反転された整数が 32 ビット符号付き整数の範囲を超える場合は [−231, 231 − 1]
0 を返します。
環境では 64 ビット整数 (符号付きまたは符号なし) の保存が許可されていないとします。
例 1:
入力: x = 123 出力: 321
例 2:
入力: x = -123 出力: -321
例 3:
入力: x = 120 出力: 21
解決策 1
毎回最後の番号を取得して、新しい番号を作成します。( Zili) 判定条件は、x自体がintで制限されるため、xが正の数で桁数がInteger.MAX_VALUEの桁数と等しい場合、最初の桁は最大でも2桁までしか取れないので、反転後は表示されません
res = Integer.MAX_VALUE / 10 && tmp > 2 の場合、res==214748364 && tmp > 7 を判定する必要はありません。逆も同様です。
class Solution {
public int reverse(int x) {
int res = 0;
while(x!=0) {
//每次取末尾数字
int tmp = x%10;
//判断是否 大于 最大32位整数
if (res > Integer.MAX_VALUE / 10 || res < Integer.MIN_VALUE / 10) {
return 0;
}
res = res*10 + tmp;
x /= 10;
}
return res;
}
}
解決策 2
最初に文字列に変換し、次に文字に変換し、最初にシンボルを判断し、次にサイクルの開始と終了を交換します。例外を処理します。
class Solution {
public int reverse(int x) {
String s=String.valueOf(x);
char[] array=s.toCharArray();
int i=0,right=s.length()-1;
char temp=' ';
if(s.charAt(i)=='+'||s.charAt(i)=='-'){
i++;
}
while(i<right){
temp=array[i];
array[i]=array[right];
array[right]=temp;
i++;
right--;
}
try{
return Integer.parseInt(String.valueOf(array));
}catch(NumberFormatException e){
return 0;
}
}
}
解決策 3 (最初の解決策と同様の公式分析)
参考バックル
反転後の数値として rev を記録します。反転を完了するには、xxx が 000 になるまで、xxx の終了番号を「ポップ」し、rev の最後に「プッシュ」することを繰り返します。
補助スタックや配列の助けを借りずに数値を「ポップ」および「プッシュ」するには、次の数学的手法を使用できます。
// x 桁の最後の桁をポップします
digit = x % 10
x /= 10
// 数値の桁をrevの最後にプッシュします
rev = rev * 10 + 桁
class Solution {
public int reverse(int x) {
int rev = 0;
while (x != 0) {
if (rev < Integer.MIN_VALUE / 10 || rev > Integer.MAX_VALUE / 10) {
return 0;
}
int digit = x % 10;
x /= 10;
rev = rev * 10 + digit;
}
return rev;
}
}
3. 文字列変換整数(atoi) (8)
文字列を 32 ビット符号付き整数に変換する関数を実装してください ( C/C++ の関数myAtoi(string s)
と同様 )。atoi
関数 のアルゴリズムはmyAtoi(string s)
次のとおりです。
- 文字列を読み取り、不要な先頭の空白を破棄します。
- 次の文字 (文字の終わりにまだ到達していないものと仮定して) がプラス記号かマイナス記号であるかを確認し、その文字 (存在する場合) を読み取ります。最終結果が陰性か陽性かを決定します。どちらも存在しない場合、結果は肯定的なものとみなされます。
- 次の非数字文字に到達するか、入力の終わりに到達するまで、次の文字を読み取ります。文字列の残りの部分は無視されます。
- 前の手順で読み取ったこれらの数値を整数に変換します (つまり、「123」 -> 123、「0032」 -> 32)。数値が読み込まれない場合、整数は です
0
。必要に応じてシンボルを変更します (手順 2 から)。 - 整数の数が 32 ビット符号付き整数の範囲を超える場合は
[−231, 231 − 1]
、この範囲内に収めるために整数を切り捨てる必要があります。具体的には、−231
より小さい整数は に固定し−231
、231 − 1
より大きい整数は に固定する必要があります231 − 1
。 - 最終結果として整数を返します。
知らせ:
- この質問の空白文字にはスペース文字のみが含まれます
' '
。 - 先頭のスペース以外の 文字や数字の後の文字列の残りの部分は省略しないでください。
例 1:
入力: s = "42" 出力: 42 説明:太字の文字列は読み取られた文字、キャレットは現在読み取られている文字です。 ステップ 1: "42" (先頭にスペースがないため、現在は文字が読み取られません) ^ ステップ 2: "42" ('-' または '+' がないため、現在は文字が読み取られません) ^ ステップ 3: " 42 " (「42」と読み取られます) ^ 解析して整数 42 を取得します。 「42」は [-231, 231 - 1] の範囲内にあるため、最終結果は 42 になります。
例 2:
入力: s = "-42" 出力: -42 説明: ステップ 1: " -42" (先頭のスペースを読み取りますが、無視します) ^ ステップ 2: " -42 " ('-' 文字を読み取るため、結果は次のようになります)負の数) ^ ステップ 3: " -42 " (「42」と読み取ります) ^ 解析して整数 -42 を取得します。 「-42」は [-231, 231 - 1] の範囲内にあるため、最終結果は -42 になります。
例 3:
入力: s = "単語付き 4193" 出力: 4193 説明: ステップ 1: "単語付き 4193" (先頭にスペースがないため、現在文字は読み取られません) ^ ステップ 2: "単語付き 4193" (現在読み取られる文字はありません)ここには '-' または '+' がないため) ^ ステップ 3: 「4193と単語」 (「4193」と読みます。次の文字は数字ではないため、読み取りは停止します) ^ 解析して整数 4193 を取得します。 「4193」は [-231, 231 - 1] の範囲内にあるため、最終結果は 4193 になります。
解決策 1
まず文字列を配列に分割して先頭のスペースを削除し、最初の文字列がシンボルの場合はトラバースして保存し、次に数値を照合し、数値でない場合はブレークします。その後、文字に対して例外処理を行い、境界や記号を判定します。
class Solution {
public int myAtoi(String s) {
int result=0,i=0;
String[] str=s.trim().split("");//去掉首部空格并分割成字符数组
String flag="";//正负符号
StringBuffer newStr=new StringBuffer();//新建一个临时存储区域
while(i<str.length){
if(str[i].equals("+")&&i==0||str[i].equals("-")&&i==0){//如果有符号则存储
flag=str[i];
i++;
continue;
}
if(str[i].matches("[0-9]+")){//用正则匹配数字
newStr.append(str[i]);
i++;
}else{
break;
}
}
try{
if(!newStr.toString().equals("")){
if(!flag.equals("")){
result=Integer.parseInt(flag+newStr.toString());
}else{
result=Integer.parseInt(newStr.toString());
}
}else{
result=0;
}
}catch(NumberFormatException e){
// System.out.println(e);
if(flag.equals("-")){//判断范围 处理异常
result=(int)Math.pow(-2,31);
}else{
result=(int)Math.pow(2,31);
}
}finally{
return result;
}
}
}
解決策 2
これは Likou の公式分析です。アルゴリズムは非常に優れており、まず状態テーブルを作成し、それをマップに格納し、文字列を走査し、状態に応じてさまざまな判断を行います。
class Solution {
public int myAtoi(String str) {
Automaton automaton = new Automaton();
int length = str.length();
for (int i = 0; i < length; ++i) {
automaton.get(str.charAt(i));
}
return (int) (automaton.sign * automaton.ans);
}
}
class Automaton {
public int sign = 1;
public long ans = 0;
private String state = "start";
private Map<String, String[]> table = new HashMap<String, String[]>() {
{
put("start", new String[]{"start", "signed", "in_number", "end"});
put("signed", new String[]{"end", "end", "in_number", "end"});
put("in_number", new String[]{"end", "end", "in_number", "end"});
put("end", new String[]{"end", "end", "end", "end"});
}};
public void get(char c) {
state = table.get(state)[get_col(c)];
if ("in_number".equals(state)) {
ans = ans * 10 + c - '0';
ans = sign == 1 ? Math.min(ans, (long) Integer.MAX_VALUE) : Math.min(ans, -(long) Integer.MIN_VALUE);
} else if ("signed".equals(state)) {
sign = c == '+' ? 1 : -1;
}
}
private int get_col(char c) {
if (c == ' ') {
return 0;
}
if (c == '+' || c == '-') {
return 1;
}
if (Character.isDigit(c)) {
return 2;
}
return 3;
}
}
問題解決3(最適解)
まず、この方法は最初のスペースを削除するという点で非常に賢明であり、すべてのスペースの極端なケースを考慮しています。
次に符号ビットを判定します。次に、文字列をトラバースし、数値を判断し、境界を判断します。
public class Solution {
public int myAtoi(String str) {
int len = str.length();
// str.charAt(i) 方法回去检查下标的合法性,一般先转换成字符数组
char[] charArray = str.toCharArray();
// 1、去除前导空格
int index = 0;
while (index < len && charArray[index] == ' ') {
index++;
}
// 2、如果已经遍历完成(针对极端用例 " ")
if (index == len) {
return 0;
}
// 3、如果出现符号字符,仅第 1 个有效,并记录正负
int sign = 1;
char firstChar = charArray[index];
if (firstChar == '+') {
index++;
} else if (firstChar == '-') {
index++;
sign = -1;
}
// 4、将后续出现的数字字符进行转换
// 不能使用 long 类型,这是题目说的
int res = 0;
while (index < len) {
char currChar = charArray[index];
// 4.1 先判断不合法的情况
if (currChar > '9' || currChar < '0') {
break;
}
// 题目中说:环境只能存储 32 位大小的有符号整数,因此,需要提前判:断乘以 10 以后是否越界
if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && (currChar - '0') > Integer.MAX_VALUE % 10)) {
return Integer.MAX_VALUE;
}
if (res < Integer.MIN_VALUE / 10 || (res == Integer.MIN_VALUE / 10 && (currChar - '0') > -(Integer.MIN_VALUE % 10))) {
return Integer.MIN_VALUE;
}
// 4.2 合法的情况下,才考虑转换,每一步都把符号位乘进去
res = res * 10 + sign * (currChar - '0');
index++;
}
return res;
}
}
解決策 4
私の意見では、解決策 3 ほど理解するのは簡単ではありません。効率は解決策 3 と一致しています。
public class Solution {
public int myAtoi(String str) {
int result = 0;
boolean negative = false;
int i = 0, len = str.length();
int limit = -Integer.MAX_VALUE;
int multmin;
int digit;
//skip space before string
while (i < len) {
if (str.charAt(i) == ' ') {
i++;
} else {
break;
}
}
if (i < len) {
char firstChar = str.charAt(i);
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+') {
return 0;
}
i++;
if (len == i) {
return 0;
}
}
multmin = limit / 10;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(str.charAt(i++), 10);
if (digit < 0) {
return negative ? result : -result;
}
if (result < multmin) {
return negative ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
result *= 10;
if (result < limit + digit) {
return negative ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
result -= digit;
}
} else {
return 0;
}
return negative ? result : -result;
}
}