1*2*...*1000的末尾有多少个0?(Java实现)

本文介绍了一种计算从1乘到特定数值(如1000)的阶乘结果中末尾零的数量的方法。通过分析质因数分解原理,得出只需关注因子5的数量即可。提供了两种实现方式:一种是通过数学算法计算因子数量;另一种是利用BigInteger进行精确计算。

/**
* 算法描述:
* 分解质因数,即:
* 1*2*3*...*1000 = (2*5)*(2*5)*...*(2*5)
                    *(没有匹配的2或5) //其实地球人都知道2肯定比5多
                                                  //这样其实只需要确定因子5的个数即可
                    *(除2和5以外的其他质因数)
* 其因子中有多少个(2*5)即末尾有多少个0
* 因此问题变成求每个数中2和5因子的个数,再求和
* 最后min(因子2个数的总和,因子5个数的总和)即是所求。
*/
public class SumZero {
  SumZero(){
   max = 1000;//默认值是1000
  }
  SumZero(int n){
   max = n;//可以通过构造函数修改max的值
  }
  
  private int max;
  /**
   * 得到max的因子 m 的个数
   */
  public int getFactor(int m){
   int  count = 0,
         n = max;
  
   //try 中的便是程序的核心
   try{
    while(n%m == 0){//能被m整除则count++
     count++;
     if(n >= m){
      n /= m;
     }
    }
   }
   catch(Exception e){
    e.printStackTrace();
   }
  
   return count;  
  }
  
  /**
   * 得到末尾0的个数
   */
  public int getNumber(){
   int count_2 = 0,//因子2的个数
        count_5 = 0;//因子5的个数

   for(int i = 1;i<=max;i++){
    count_2 += getFactor(i,2);//其实没有必要求因子2的个数
    count_5 += getFactor(i,5);
   }
  
   return Math.min(count_2,count_5);//必定有:count_5 < count_2
  }
  public static void main(String[] args){
   System.out.println("1*2*...*1000的末尾有"
                                     +new SumZero().getNumber()
                                     +"个0");
  }
}

///另一种做法(用 BigInteger)

By:believefym

import java.math.*;
public class OneK {
    public static void main(String[] args) {
        BigInteger b = BigInteger.valueOf(1);
        for(int i=1; i<=1000; i++){
            b = b.multiply(BigInteger.valueOf(i));
        }
        String tmp = b.toString();
        int counter=0;
        for(int i=tmp.length()-1; i>0; i--){
            if(tmp.charAt(i)=='0')
                counter++;
            else
                break;
        }
        System.out.println(counter);//249
    }
}

<think> 我们正在讨论的问题是:为什么在JavaScript和Java中使用正则表达式`\{(.*?)\}`时,匹配结果会出现不一致。 根据引用[1]和[2]的内容,我们知道`.*?`是非贪婪匹配(懒惰模式),会匹配尽可能少的字符。 然而,在JavaScript和Java中,虽然都支持非贪婪匹配,但它们在处理匹配的方式上可能存在差异,尤其是在多行匹配和点号(.)是否匹配换行符的问题上。 具体分析: 1.JavaScript中,正则表达式默认情况下点号(.)不匹配换行符(\n)。因此,如果我们的文本中包含换行符,那么`.*?`在遇到换行符时就会停止匹配。 2.Java中,默认情况下点号(.)也不匹配换行符。但是,Java中我们可以通过设置`Pattern.DOTALL`标志来让点号匹配包括换行符在内的所有字符。而JavaScript中可以通过在正则表达式末尾添加`s`标志(ES2018引入)来达到同样效果(例如`/.*?/s`),但如果不使用这个标志,点号就不会匹配换行符。 因此,如果文本中有换行符,并且我们没有设置相应的标志,那么: - 在JavaScript中,`\{(.*?)\}`可能只会匹配在同一行内的内容,因为点号不匹配换行符,所以遇到换行符就停止了。 - 在Java中,如果我们没有使用`Pattern.DOTALL`,那么同样不会匹配跨行的内容。 但是,这里的不一致可能不仅仅是换行符的问题,还可能因为两个引擎在非贪婪匹配的具体实现上存在差异(尽管标准相同,但实现细节可能导致不同)。 另一个可能的原因:匹配的文本中可能存在多个花括号对,非贪婪匹配在不同引擎中的回溯机制可能导致匹配到不同的花括号对。 例如,考虑以下文本: "第一个 {value1} 第二个 {value2}" 使用`\{(.*?)\}`: - 在JavaScript中,它会匹配`{value1}`,因为非贪婪匹配会尽快找到第一个`}`。 - 在Java中,同样会匹配`{value1}`。 所以,如果出现不一致,很可能是由于换行符的存在,或者文本中有嵌套的花括号。 对于嵌套花括号的情况,非贪婪匹配会匹配到第一个遇到的结束花括号,所以不会匹配整个嵌套结构。例如: 文本: "{outer {inner} outer}" 使用`\{(.*?)\}`: - 匹配到的内容会是`outer {inner`(如果点号不匹配换行符,并且没有其他特殊标志),然后遇到第一个`}`就结束,得到`{outer {inner}`。但实际上,我们可能希望匹配整个嵌套结构(但这需要更复杂的正则表达式,因为简单的非贪婪匹配无法处理嵌套)。 然而,如果文本中嵌套了花括号,那么非贪婪匹配会匹配到第一个结束花括号,所以结果可能不是我们想要的。这在两种语言中行为应该是一致的。 因此,如果出现不一致,最可能的原因是: 1. 点号(.)是否匹配换行符:检查文本中是否有换行符,以及正则表达式是否设置了对应的标志(在Java中是Pattern.DOTALL,在JavaScript中是`s`标志)。 2. 正则表达式的写法:在Java中,我们通常需要将正则表达式字符串进行转义(因为Java字符串中反斜杠是转义字符),所以写的时候要写成`"\\{(.*?)\\}"`。而在JavaScript中,我们直接写`/\{(.*?)\}/`或`new RegExp("\\{(.*?)\\}")`。 例如,在Java中,如果我们这样写: Pattern pattern = Pattern.compile("\\{(.*?)\\}"); Matcher matcher = pattern.matcher(text); 如果text中包含换行,并且我们希望点号匹配换行符,那么需要: Pattern pattern = Pattern.compile("\\{(.*?)\\}", Pattern.DOTALL); 在JavaScript中,如果我们使用字面量: let regex = /\{(.*?)\}/; // 点号不匹配换行符 let regex = /\{(.*?)\}/s; // 点号匹配换行符(ES2018支持) 或者使用构造函数: let regex = new RegExp("\\{(.*?)\\}", "s"); // 使用s标志 所以,不一致的原因可能是: - 正则表达式标志的设置不同(主要是是否允许点号匹配换行符) - 或者是因为转义的问题导致正则表达式本身不同(例如Java字符串中需要双反斜杠) 另外,如果文本中有多个匹配,我们在提取的时候也要注意提取的方式。在Java中,我们通常使用`Matcher`对象循环查找,而在JavaScript中,我们可以使用`match`方法(当正则表达式有`g`标志时返回所有匹配的数组)或`exec`方法循环。 因此,请检查以下方面: 1. 正则表达式是否设置了对换行符匹配的标志(DOTALL或s)。 2. 文本中是否有换行符。 3. 是否有嵌套的花括号,导致非贪婪匹配提前结束(但这种情况在两种语言中行为应该一致,都是匹配到第一个结束花括号)。 4. 提取匹配的方式:Java中需要循环调用`matcher.find()`,而JavaScript中如果有`g`标志,则`match`方法返回所有匹配的数组(但注意:在JavaScript中,`match`方法使用`g`标志时不会返回捕获组,需要使用`matchAll`)。 另外,对于捕获组的内容: - 在Java中,我们可以通过`matcher.group(1)`获取第一个捕获组。 - 在JavaScript中,我们可以通过`match[1]`获取(当没有`g`标志时)或者通过`exec`的结果数组的索引1获取。 所以,如果提取方式不对,也可能导致我们获取的结果不一致。 总结: 不一致的主要原因可能是: a) 正则表达式标志(是否允许点号匹配换行符)设置不同。 b) 正则表达式字符串在Java中需要双重转义。 c) 匹配的文本中包含换行符。 d) 提取匹配的方式不同(例如,在JavaScript中使用了`match`并且加了`g`标志,但是这样不会返回捕获组,所以我们需要用`matchAll`或者`exec`)。 建议: 1. 检查文本中是否有换行符,如果有,考虑在两种语言中都设置对应的标志(DOTALL或s)。 2. 确保正则表达式字符串的转义正确(Java中双反斜杠,JavaScript中单反斜杠在字符串中需要写成双反斜杠)。 3. 检查匹配的提取方式。 示例代码: Java(使用DOTALL): String text = "内容..."; Pattern pattern = Pattern.compile("\\{(.*?)\\}", Pattern.DOTALL); Matcher matcher = pattern.matcher(text); while (matcher.find()) { System.out.println(matcher.group(1)); // 输出捕获组 } JavaScript(使用`s`标志和`matchAll`): let text = "内容..."; let regex = /\{(.*?)\}/gs; // 注意:如果要匹配多个,需要g标志;s标志让点号匹配换行 let matches = [...text.matchAll(regex)]; matches.forEach(match => console.log(match[1])); 或者使用`exec`: let regex = /\{(.*?)\}/s; // 如果不加g,则只能匹配第一个 let match; while ((match = regex.exec(text)) !== null) { console.log(match[1]); } 注意:在JavaScript中,`matchAll`要求正则表达式必须设置`g`标志,否则会抛出异常。 因此,请根据上述建议检查代码,确保在两种语言中的正则表达式等效(包括标志)以及匹配提取方式正确。 </think> ### JavaScript 与 Java 正则表达式 `\{(.*?)\}` 匹配结果不一致的原因分析 在 JavaScript 和 Java 中使用 `\{(.*?)\}` 正则表达式时,匹配结果可能因**正则引擎差异****默认行为设置**而不同。主要区别如下: --- #### 1. **点号`.`的匹配范围差异(核心原因)** - **JavaScript**:默认情况下,点号`.` **不匹配换行符** `\n` ```javascript // JS 示例:不匹配跨行内容 const text = "{第一行\n第二行}"; const regex = /\{(.*?)\}/; console.log(text.match(regex)); // 匹配失败:null(因为 .*? 遇到换行符停止) ``` - **Java**:可通过 `Pattern.DOTALL` 标志使`.`匹配**包括换行符**的所有字符 ```java // Java 示例:设置 DOTALL 标志后匹配跨行内容 String text = "{第一行\n第二行}"; Pattern pattern = Pattern.compile("\\{(.*?)\\}", Pattern.DOTALL); Matcher matcher = pattern.matcher(text); while (matcher.find()) { System.out.println(matcher.group(1)); // 输出:第一行\n第二行 } ``` *若未显式设置 `Pattern.DOTALL`,Java 默认行为与 JS 相同(不匹配换行符)[^1]* --- #### 2. **贪婪/非贪婪模式实现差异** 虽然 `.*?` 在两种语言中都表示**非贪婪匹配**,但引擎优化策略不同: - **JavaScript**:V8 引擎会尽可能**缩短匹配结果** ```text 文本: "{a}{b}{c}" JS: \{(.*?)\} → 匹配三次 ["{a}", "{b}", "{c}"] ``` - **Java**:在某些嵌套场景下可能返回更长匹配 ```text 文本: "{outer {inner} end}" Java: \{(.*?)\} 可能匹配整个 "{outer {inner} end}" 而非预期的两次匹配 ([^2]) ``` --- #### 3. **转义字符处理差异** - **Java** 需要双重转义正则表达式中的反斜杠: ```java // 正确写法 Pattern.compile("\\{(.*?)\\}"); ``` 单反斜杠写法 `\{(.*?)\}` 会导致编译错误 - **JavaScript** 只需单次转义: ```javascript // 正确写法 /\{(.*?)\}/ ``` --- #### 4. **解决方案:确保跨语言一致性** | 策略 | JavaScript 实现 | Java 实现 | |---------------------|------------------------|------------------------------| | **匹配跨行内容** | 添加 `s` 标志: `/\{(.*?)\}/s` | 设置 `Pattern.DOTALL` | | **明确贪婪控制** | 使用 `[^}]` 替代 `.`: `/\{([^}]*)\}/` | `\\{([^}]*)\\}` | | **统一转义规则** | `/\{(.*?)\}/` | `Pattern.compile("\\{(.*?)\\}")` | --- #### 验证示例 假设文本为 `{Line1\nLine2}`: ```javascript // JavaScript(正确匹配) const matchJS = text.match(/\{(.*?)\}/s); // 捕获组: "Line1\nLine2" ``` ```java // Java(正确匹配) Pattern p = Pattern.compile("\\{(.*?)\\}", Pattern.DOTALL); Matcher m = p.matcher(text); m.find(); m.group(1); // 返回 "Line1\nLine2" ``` > 关键点:**显式指定匹配模式**(如 DOTALL/s 标志)和**避免依赖引擎默认行为**可解决 90% 的跨语言不一致问题[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值