普通视图

发现新文章,点击刷新页面。
今天 — 2026年5月13日首页

谷歌开了一场「读作 Android,写作 Gemini」的硬件发布会

2026年5月13日 03:02
Image
用重新押注 Android,给 Gemini 搭建一套硬件「骨架」

作者|张勇毅

2026年5月13日,作为每年 Google I/O 的前哨站,同时也是关于最重要的部分——安卓的独立发布会,The Android Show在线上开幕,揭开了 2026 年 Google 在 Android 领域全系产品阵容的新品发布阵容。

Image

如果你只看 Android Show 2026 的发布内容,第一反应可能是:这真的是一场 Android 发布会吗?

无论是 Gemini 的自动操作,还是用 Gemini 生成桌面小组件,甚至是由 AI 驱动的语音输入法 ——被 Google 反复演示的这些功能,本质上都不是 Android 自己的功能,而是 Gemini 的延伸。Gemini Intelligence 是新名字,Googlebooks 是新硬件品类,连 Chrome 和 Android Auto 都成了 Gemini 的入口。把「Android」这个词从所有发布稿里抽掉,剩下的内容其实只在讲一件事——Gemini。

这是一场看似 Android、实际上是 Gemini 的发布会。Android,是 Gemini 今天选择 All in 落地的那副「硬件骨架」。

过去几年,这副骨架其实已经被 Google 冷落了一阵子 —— Gemini 真正的主战场是网页端、Workspace、搜索,Gemini 应用在 iOS 和 Android 上的体验也几乎一样。

这一次,Google 把它重新拉了出来。

至于骨架的形状怎么搭——18 个月前,苹果已经给过一份样图。

Google 已经连续第二年把 Android 发布会从 I/O 主舞台拆出来,单独放在 I/O 前一周。这个日程安排本身,比这场发布会的任何一个演示都更值得分析——它意味着 Android 已经不能在 I/O 主舞台上和 Gemini 模型并列,但又必须有自己的舞台。

这次 The Android Show 一共发布了四组内容:Gemini Intelligence、Googlebooks、Android 17 系统更新、Android Auto 车机更新。

Image

后两组里几乎没什么真正的新东西。Android 17 那批面向创作者的功能——Screen Reactions 同时录人和屏幕、Instagram 终于做了 Android 平板优化、Adobe Premiere 来到 Android——更像是迟到的修补。还有一个叫 Pause Point 的数字健康功能,允许用户在点开应用时停 10 秒,问自己一句「我为什么打开这个应用」。以及 Noto 3D,emoji 表情的重新设计。Android Auto 是 Material 3 Expressive 重设计加 Dolby Atmos 空间音频,是一次常规迭代。

这些功能放在任何一年的 Android 春季发布会上,都不会让人意外。

但 Gemini Intelligence 不一样。它不是一个功能,而是一个名字——一个把 Google 这些年所有 AI 努力打包起来、并明确划出「谁有资格用、谁没有」的伞形品牌。

Image

Googlebooks 也不是一台新笔电那么简单。它是 Google 为了承载 Gemini Intelligence,专门造的一个新硬件品类——拉来 Acer、Asus、Dell、HP、Lenovo 这五家,覆盖了从 Chromebook 老朋友到传统 Windows PC 大厂的全部主流厂商。

发布会真正值得拆解的,就是这两件事。

回到 2024 年 6 月那个 WWDC——Apple Intelligence 出场的那一刻,苹果做的最关键的一件事其实不是任何功能演示,而是把一整套 AI 体验和 iPhone 15 Pro 之后的设备硬绑在一起。下一代手机要买 Pro 才能用上完整的 AI,老一代 iPad 大部分被排除在外,连 Mac 这边也是 M1 之后才行。

这个动作当时被很多人解读为「苹果为了卖旗舰机」的市场策略。但放到现在看,它做的事情远不止营销层面——它定义了一个 AI 时代的产品范式:AI 不是装在每台设备上的软件,是只有有着充沛本地算力的设备才有资格享受的硬件特权。

Google 之前走的恰好是相反的路。Gemini 一直是一个非常「Google」式的产品——它是网页端产品,是开发者接口,是手机上一个能下载的应用,是融进 Workspace、Search、YouTube 的能力层。Google 反复强调「任何 Android 设备都能用 Gemini」,Gemini Nano 推到中端机型上,免费层做得也很厚。这是 Android 一贯的姿态——开放、低门槛、设备无差别。

Gemini Intelligence 这个名字一出来,姿态就变了。

看 Google 自己的措辞就够清楚——「高端 Android 设备」、「最新一代 Pixel 和三星 Galaxy」、「今夏上线」。第一波用户是最新一代的 Pixel 和 Galaxy,手表、汽车、眼镜、笔电之后跟上。一条非常苹果味的高端设备路线图。

Image

具体功能也都按这个路线设计。Gboard 的「Rambler」会用 Gemini 升级语音输入,过滤掉填充词、停顿、自我修正,让你直接说出来的内容像写出来的。

Image

Create My Widget 让你描述一个想要的小组件——比如「每周给我推荐三个高蛋白健身餐」——AI 直接生成一个自定义小组件,放进桌面。

Image

Chrome 终于有了「自动浏览」,可以代你在网页上滚动、点击、填表单,去 SpotHero 订停车位,去 Chewy 把狗粮订单从幼犬粮换成成犬粮——这是手机端智能体在 Android 上第一次实际落地。

这些功能确实需要算力,需要系统级权限,需要硬件配合。所以「高端门槛」在工程上有合理性。但这个合理性苹果早就用过一次了。

Googlebooks 是这个范式的极致表达。

Google 这一次没有满足于给现有硬件分级,而是直接为 Gemini Intelligence 量身做了一个新硬件品类。Magic Pointer 让你用鼠标指任何东西,唤起 Gemini 上下文交互;Create My Widget 也来到桌面;Cast my apps 把手机应用投到笔电大屏上;Quick Access 让笔电直接浏览手机里的文件。所有这些功能,离开 Gemini 都不成立。

Image

厂商合作伙伴名单尤其值得拆开看。Acer、Asus 是 Chromebook 老朋友,但 Dell、HP、Lenovo 这三家,是传统 Windows 电脑大厂,他们之前在 Chromebook 阵营里几乎可以忽略不计。

Google 这次把传统电脑厂商也拉进来,意图很清楚:用 Gemini Intelligence 重新打开电脑市场的入场券——而 Chromebook 14 年没能撼动 Windows 的旧战场,这次它想用 AI 这张牌再开一局。

把 Gemini Intelligence 的命名 + 高端硬件路线图 + Googlebooks 这个量身新品类三件事放在一起看,Google 走的就是 2024 年苹果走过的那条路。这是一次承认——AI 时代的胜负,不是在云端模型上分的,是在硬件层面上分的。

 

但这个路线图在中国,几乎插不下去。

Apple Intelligence 之所以能成立,前提是苹果同时掌握 iOS 和硬件——「高端门槛」的边界,由苹果自己说了算。iPhone 15 Pro 是不是高端、A17 Pro 是不是门槛,苹果一句话就定了。

Android 不是这个结构。

2024 年 1 月,三星在 Galaxy S24 的发布会上推出了 Galaxy AI——那是三星自己的 AI 子品牌,基于 Galaxy 设备自研的功能(实时翻译、Note Assist、生成式编辑)。Galaxy AI 用了两年时间,已经成了三星高端机销售故事的核心。这一次 Gemini Intelligence 要靠最新的 Galaxy 设备首发,但 Galaxy S26 上的 AI 故事,必须姓「Samsung」,不能姓「Google」。

Image

中国市场更直接——这里几乎没有 Gemini Intelligence 的发挥空间。

每一家中国厂商都有自己的 AI 战略:小米澎湃 OS 上的端侧 AI 能力、OPPO 的 AndesGPT、vivo 的蓝心大模型、华为的盘古。它们的 AI 系统是端云结合的自研架构,模型层是阿里通义、百度文心、字节豆包的混合方案,端侧推理也基于自家芯片优化。Google 服务在中国市场全面缺位,Gemini 模型既进不来,也插不进来。中国厂商的 AI 故事和 Google 几乎完全脱钩——一台国产旗舰手机上的 AI,从模型到品牌,都和 Google 没有关系。

这意味着,Google 这一次的「Android 阵营 AI 标准制定者」野心,实际能号召的盘子比想象中小很多 —— Pixel(在全球 Android 市场只占小份额的设备),加上部分三星 Galaxy(要被分享话语权),再加上一些跟随 Google 节奏的厂商,谷歌在 Gemini 上的这套应用更多是一种标准模板,提供给更多的中国厂商。

厂商各自的 AI 战略已经成型,Google 这面旗帜插下去,土地早就被占了。


12 年前,Android 用「任何价位、任何屏幕尺寸、任何配置」撕开了 iOS 把控的高端市场。那是它当年最强的武器——开放、低门槛、设备无差别。

12 年后,Google 重新押注 Android,但这一次是把它当作 Gemini 的硬件骨架——一副长得越来越像 iOS 的骨架。

这不是退化,是 AI 时代 OS 厂商话语权的重新分配。只是这一次,Android 走向了自己当年的反面——而且,它已经不再只听 Google 一个人的了。

*头图来源:Google
本文为极客公园原创文章,转载请联系极客君微信 geekparkGO
极客一问
你怎么看?
Image
Image
Image
Image

 

How to Install LEMP Stack on Ubuntu 26.04

When you need to host a PHP application or a CMS such as WordPress, Magento, or Laravel on a fresh Ubuntu server, the LEMP stack is one of the most common ways to get there. LEMP stands for Linux, Nginx (pronounced “engine-x”), MySQL, and PHP, and the four pieces fit together to serve dynamic PHP pages over HTTP.

This guide explains how to install and configure a complete LEMP stack on Ubuntu 26.04. By the end you will have Nginx serving HTTP traffic, MySQL 8.4 running as the database, and PHP 8.5 processing dynamic pages through PHP-FPM.

Quick Reference

Task Command
Update package index sudo apt update
Install Nginx sudo apt install nginx
Install MySQL sudo apt install mysql-server
Install PHP-FPM sudo apt install php-fpm php-mysql
Secure MySQL sudo mysql_secure_installation
Restart PHP-FPM sudo systemctl restart php8.5-fpm
Reload Nginx sudo systemctl reload nginx
Test Nginx config sudo nginx -t
Allow HTTP/HTTPS sudo ufw allow 'Nginx Full'
PHP-FPM socket /run/php/php8.5-fpm.sock

Prerequisites

Before installing the stack, make sure you have:

Step 1: Install Nginx

Nginx is in the default Ubuntu 26.04 repositories. Refresh the package index and install it:

Terminal
sudo apt update
sudo apt install nginx

Once the install finishes, the service starts automatically. Confirm it is running:

Terminal
sudo systemctl status nginx

The output shows the service as active (running). If UFW is enabled, allow HTTP and HTTPS traffic:

Terminal
sudo ufw allow 'Nginx Full'

Open http://your_server_ip in a browser. You should see the default Nginx welcome page, which confirms that Nginx is serving requests.

For a deeper walkthrough of the install and the directory layout, see How to Install Nginx on Ubuntu 26.04 .

Step 2: Install MySQL

The database tier handles persistence. Install the MySQL server package:

Terminal
sudo apt install mysql-server

Once the install completes, run the security script. It walks you through removing anonymous users, disabling remote root login, and dropping the test database:

Terminal
sudo mysql_secure_installation

When prompted, answer Y to each hardening question. The validate password component is optional and you can disable it if you plan to manage credentials yourself.

To verify that the service is running:

Terminal
sudo systemctl status mysql

For details on creating users and granting privileges, see How to Install MySQL on Ubuntu 26.04 .

Step 3: Install PHP and PHP-FPM

Nginx does not embed a PHP interpreter. Instead, requests for PHP files are handed off to PHP-FPM (FastCGI Process Manager) over a Unix socket. Install PHP-FPM and the MySQL driver:

Terminal
sudo apt update
sudo apt install php-fpm php-mysql

On Ubuntu 26.04 this pulls in PHP 8.5. Confirm the version:

Terminal
php -v

The output starts with PHP 8.5.x. The PHP-FPM service starts automatically and listens on a Unix socket at /run/php/php8.5-fpm.sock:

Terminal
sudo systemctl status php8.5-fpm

If you need extra extensions such as php-curl, php-gd, php-mbstring, php-xml, or php-zip, install them now:

Terminal
sudo apt install php-curl php-gd php-mbstring php-xml php-zip

For information on additional modules and switching PHP versions, see How to Install PHP on Ubuntu 26.04 .

Step 4: Configure Nginx to Serve PHP

Now that Nginx and PHP-FPM are running, you need a server block that passes .php requests to PHP-FPM. Create a directory for the site and a small test file:

Terminal
sudo mkdir -p /var/www/example.com
sudo chown -R $USER:$USER /var/www/example.com

Create a new server block configuration:

Terminal
sudo nano /etc/nginx/sites-available/example.com

Paste the following content. Replace example.com with your domain or your server IP if you do not have a domain yet:

nginx
server {
 listen 80;
 listen [::]:80;

 server_name example.com www.example.com;
 root /var/www/example.com;

 index index.php index.html;

 location / {
 try_files $uri $uri/ =404;
 }

 location ~ \.php$ {
 include snippets/fastcgi-php.conf;
 fastcgi_pass unix:/run/php/php8.5-fpm.sock;
 }

 location ~ /\.ht {
 deny all;
 }
}

The try_files directive serves static files when they exist and falls back to a 404 otherwise. The location ~ \.php$ block matches any request ending in .php and forwards it to PHP-FPM through the Unix socket. The final location ~ /\.ht block blocks access to legacy .htaccess files, which Nginx does not use.

Enable the site by creating a symlink in sites-enabled:

Terminal
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

If the default server block is still enabled, remove its symlink so Nginx serves your new site for matching requests:

Terminal
sudo unlink /etc/nginx/sites-enabled/default

Test the configuration before reloading:

Terminal
sudo nginx -t

The output should end with syntax is ok and test is successful. Reload Nginx to apply the change:

Terminal
sudo systemctl reload nginx

Step 5: Test the PHP Setup

Create a simple PHP info file to confirm that Nginx hands PHP requests to PHP-FPM:

Terminal
echo "<?php phpinfo();" | sudo tee /var/www/example.com/info.php

Open http://example.com/info.php (or http://your_server_ip/info.php) in a browser. You should see the PHP information page that lists the PHP version, loaded extensions, and configuration directives. If the page shows the source code as plain text, Nginx is not passing the request to PHP-FPM and the location ~ \.php$ block needs to be reviewed.

Once you confirm PHP is working, remove the info file. It exposes details about your environment that should not be publicly readable:

Terminal
sudo rm /var/www/example.com/info.php

Step 6: Test the MySQL Connection from PHP

To confirm that PHP can talk to MySQL, create a test database and user:

Terminal
sudo mysql

In the MySQL prompt, run:

sql
CREATE DATABASE lemp_test;
CREATE USER 'lemp_user'@'localhost' IDENTIFIED BY 'changeme';
GRANT ALL PRIVILEGES ON lemp_test.* TO 'lemp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Warning
The password above is a placeholder. Replace changeme with a strong value, and do not commit credentials to version control. Store secrets in environment variables or a .env file outside the repository.

Create a small connection test:

Terminal
sudo nano /var/www/example.com/db-test.php

Add the following content:

php
<?php
$mysqli = new mysqli("localhost", "lemp_user", "changeme", "lemp_test");
if ($mysqli->connect_error) {
 die("Connection failed: " . $mysqli->connect_error);
}
echo "Connected to MySQL " . $mysqli->server_info;
$mysqli->close();

Open http://example.com/db-test.php. The page prints Connected to MySQL 8.4.x when the credentials and the PHP MySQL driver are working. Remove the file once you confirm the connection:

Terminal
sudo rm /var/www/example.com/db-test.php

Troubleshooting

Nginx shows the welcome page after editing the server block
Nginx still serves the default site. Remove the symlink at /etc/nginx/sites-enabled/default or rename your server block so it loads first, then run sudo nginx -t and sudo systemctl reload nginx.

PHP files download instead of executing
Nginx is not forwarding .php requests to PHP-FPM. Verify that the location ~ \.php$ block exists, that fastcgi_pass points to /run/php/php8.5-fpm.sock, and that PHP-FPM is running with sudo systemctl status php8.5-fpm.

502 Bad Gateway
PHP-FPM is not running, or Nginx is pointing at a wrong socket path. Run ls -l /run/php/ and confirm that the socket file matches the version in your config (for example, php8.5-fpm.sock). Restart the service with sudo systemctl restart php8.5-fpm.

Permission denied accessing the socket
The socket is owned by www-data by default. Make sure Nginx runs as www-data (the default on Ubuntu) and that no custom file permissions on /run/php/ block access.

MySQL connection refused from PHP
Confirm that the php-mysql package is installed, that the user has been granted privileges on the target database, and that the password in the PHP code matches the one used in CREATE USER.

FAQ

What is the difference between LEMP and LAMP?
LEMP uses Nginx as the web server, while LAMP uses Apache. Nginx handles many concurrent connections with low memory and is a strong fit for static content and reverse proxying. Apache supports per-directory .htaccess files and mod_php, which simplifies shared hosting setups. For a step-by-step Apache install, see How to Install LAMP Stack on Ubuntu 26.04 .

Can I use MariaDB instead of MySQL?
Yes. MariaDB is a drop-in replacement for MySQL and uses the same client tools, the same protocol, and the same SQL syntax for the cases covered in this guide.

Which PHP-FPM socket path do I use?
The path follows the PHP version. On Ubuntu 26.04 with the default PHP 8.5, the socket is /run/php/php8.5-fpm.sock. If you install another version, list /run/php/ to find the right socket file.

How do I add HTTPS to the site?
The recommended path is to install Certbot and request a Let’s Encrypt certificate for your domain. The Certbot Nginx plugin updates the server block automatically and sets up redirection from HTTP to HTTPS.

Next Steps

You now have a working LEMP stack on Ubuntu 26.04. From here you can deploy a PHP application, create additional Nginx server blocks for more sites, or front the stack with HTTPS using Let’s Encrypt.

How to Install LAMP Stack on Ubuntu 26.04

When you spin up a new server to run a PHP application or a CMS such as WordPress, Joomla, or Drupal, the LAMP stack is one of the most familiar starting points. LAMP stands for Linux, Apache, MySQL, and PHP, and the four pieces work together to serve dynamic web pages over HTTP.

This guide explains how to install and configure a complete LAMP stack on Ubuntu 26.04. By the end you will have Apache serving HTTP traffic, MySQL 8.4 running as the database, and PHP 8.5 processing dynamic pages through mod_php.

Quick Reference

Task Command
Update package index sudo apt update
Install Apache sudo apt install apache2
Install MySQL sudo apt install mysql-server
Install PHP with Apache sudo apt install php libapache2-mod-php php-mysql
Secure MySQL sudo mysql_secure_installation
Restart Apache sudo systemctl restart apache2
Test Apache config sudo apache2ctl configtest
Allow HTTP/HTTPS sudo ufw allow 'Apache Full'
Enable a site sudo a2ensite example.com
Disable default site sudo a2dissite 000-default

Prerequisites

Before installing the stack, make sure you have:

Step 1: Install Apache

Apache is in the default Ubuntu 26.04 repositories. Refresh the package index and install it:

Terminal
sudo apt update
sudo apt install apache2

The service starts automatically after the install. Confirm it is running:

Terminal
sudo systemctl status apache2

The output shows the service as active (running). If UFW is enabled, allow HTTP and HTTPS through the firewall:

Terminal
sudo ufw allow 'Apache Full'

Open http://your_server_ip in a browser. You should see the default Apache welcome page, which confirms that Apache is serving requests.

For an in-depth walkthrough of the install and the directory layout, see How to Install Apache on Ubuntu 26.04 .

Step 2: Install MySQL

The database tier handles persistence. Install the MySQL server package:

Terminal
sudo apt install mysql-server

Once the install completes, run the security script. It walks you through removing anonymous users, disabling remote root login, and dropping the test database:

Terminal
sudo mysql_secure_installation

Answer Y to each hardening question. The validate password component is optional; skip it if you plan to manage credentials yourself.

To verify that the service is running:

Terminal
sudo systemctl status mysql

For details on creating users and granting privileges, see How to Install MySQL on Ubuntu 26.04 .

Step 3: Install PHP

Apache integrates with PHP through the libapache2-mod-php module, which lets Apache run PHP code in-process. Install PHP, the Apache module, and the MySQL driver:

Terminal
sudo apt update
sudo apt install php libapache2-mod-php php-mysql

On Ubuntu 26.04 this pulls in PHP 8.5. Confirm the version:

Terminal
php -v

The output starts with PHP 8.5.x. Restart Apache so the PHP module loads:

Terminal
sudo systemctl restart apache2

If you need extra extensions such as php-curl, php-gd, php-mbstring, php-xml, or php-zip, install them now:

Terminal
sudo apt install php-curl php-gd php-mbstring php-xml php-zip

For information on additional modules and switching PHP versions, see How to Install PHP on Ubuntu 26.04 .

Step 4: Configure an Apache Virtual Host

The default Apache configuration serves files from /var/www/html. For a real site you usually want a dedicated document root and a virtual host. Create the directory and a small placeholder:

Terminal
sudo mkdir -p /var/www/example.com
sudo chown -R $USER:$USER /var/www/example.com
echo "<h1>example.com is live</h1>" > /var/www/example.com/index.html

Create a new virtual host file:

Terminal
sudo nano /etc/apache2/sites-available/example.com.conf

Paste the following content. Replace example.com with your domain or your server IP if you do not have a domain yet:

apache
<VirtualHost *:80>
 ServerName example.com
 ServerAlias www.example.com
 ServerAdmin webmaster@example.com
 DocumentRoot /var/www/example.com

 <Directory /var/www/example.com>
 Options -Indexes +FollowSymLinks
 AllowOverride All
 Require all granted
 </Directory>

 ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
 CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>

AllowOverride All lets you use per-directory .htaccess files, which is required by many PHP applications such as WordPress for clean URL rewrites. Enable the site and disable the default one:

Terminal
sudo a2ensite example.com
sudo a2dissite 000-default

Make sure mod_rewrite is enabled if your application uses it:

Terminal
sudo a2enmod rewrite

Test the configuration before reloading Apache:

Terminal
sudo apache2ctl configtest

The output should end with Syntax OK. Reload Apache to apply the change:

Terminal
sudo systemctl reload apache2

Step 5: Test the PHP Setup

Create a PHP info file to confirm that Apache executes PHP:

Terminal
echo "<?php phpinfo();" | sudo tee /var/www/example.com/info.php

Open http://example.com/info.php (or http://your_server_ip/info.php) in a browser. You should see the PHP information page that lists the PHP version, loaded extensions, and configuration directives. If the browser downloads the file or shows the source code, the PHP module is not loaded; reinstall libapache2-mod-php and restart Apache.

Once you confirm PHP is working, remove the info file. It exposes details about your environment that should not be publicly readable:

Terminal
sudo rm /var/www/example.com/info.php

Step 6: Test the MySQL Connection from PHP

To confirm that PHP can talk to MySQL, create a test database and user:

Terminal
sudo mysql

In the MySQL prompt, run:

sql
CREATE DATABASE lamp_test;
CREATE USER 'lamp_user'@'localhost' IDENTIFIED BY 'changeme';
GRANT ALL PRIVILEGES ON lamp_test.* TO 'lamp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Warning
The password above is a placeholder. Replace changeme with a strong value, and do not commit credentials to version control. Store secrets in environment variables or a .env file outside the repository.

Create a small connection test:

Terminal
sudo nano /var/www/example.com/db-test.php

Add the following content:

php
<?php
$mysqli = new mysqli("localhost", "lamp_user", "changeme", "lamp_test");
if ($mysqli->connect_error) {
 die("Connection failed: " . $mysqli->connect_error);
}
echo "Connected to MySQL " . $mysqli->server_info;
$mysqli->close();

Open http://example.com/db-test.php. The page prints Connected to MySQL 8.4.x when the credentials and the PHP MySQL driver are working. Remove the file once you confirm the connection:

Terminal
sudo rm /var/www/example.com/db-test.php

Troubleshooting

Apache shows the default page after editing the virtual host
The default site is still enabled. Run sudo a2dissite 000-default, then sudo a2ensite example.com, and reload Apache.

PHP files download instead of executing
The Apache PHP module is not loaded. Reinstall it with sudo apt install --reinstall libapache2-mod-php, enable it with sudo a2enmod php8.5, and restart Apache.

AllowOverride All is ignored
The <Directory> block in the virtual host must include AllowOverride All. Confirm that the path matches your DocumentRoot and run sudo apache2ctl configtest after the change.

.htaccess rewrites do not work
The rewrite module is not enabled. Run sudo a2enmod rewrite and restart Apache. Verify that the <Directory> block sets AllowOverride All.

MySQL connection refused from PHP
Confirm that the php-mysql package is installed, that the user has been granted privileges on the target database, and that the password in the PHP code matches the one used in CREATE USER.

FAQ

What is the difference between LAMP and LEMP?
LAMP uses Apache as the web server, while LEMP uses Nginx. Apache supports .htaccess overrides and mod_php, which makes shared hosting and many PHP CMS workflows simpler. Nginx is event-driven and tends to use less memory under high concurrency. For an Nginx-based stack, see How to Install LEMP Stack on Ubuntu 26.04 .

Can I use MariaDB instead of MySQL?
Yes. MariaDB is a drop-in replacement for MySQL and uses the same client tools, the same protocol, and the same SQL syntax for the cases covered in this guide.

Should I use mod_php or PHP-FPM with Apache?
mod_php is the simplest setup and is fine for many sites. For higher concurrency or when you run multiple PHP versions, switch to PHP-FPM with mpm_event and proxy_fcgi. The php-fpm package and the proxy_fcgi Apache module handle the connection.

How do I add HTTPS to the site?
Install Certbot and request a Let’s Encrypt certificate for your domain. The Certbot Apache plugin updates the virtual host automatically and sets up redirection from HTTP to HTTPS.

Next Steps

You now have a working LAMP stack on Ubuntu 26.04. From here you can deploy a PHP application, create additional Apache virtual hosts for more sites, or add HTTPS using Let’s Encrypt.

每日一题-使数组互补的最少操作次数🟡

2026年5月13日 00:00

给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。

如果对于所有下标 i下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 inums[i] + nums[n - 1 - i] = 5

返回使数组 互补最少 操作次数。

 

示例 1:

输入:nums = [1,2,4,3], limit = 4
输出:1
解释:经过 1 次操作,你可以将数组 nums 变成 [1,2,2,3](加粗元素是变更的数字):
nums[0] + nums[3] = 1 + 3 = 4.
nums[1] + nums[2] = 2 + 2 = 4.
nums[2] + nums[1] = 2 + 2 = 4.
nums[3] + nums[0] = 3 + 1 = 4.
对于每个 i ,nums[i] + nums[n-1-i] = 4 ,所以 nums 是互补的。

示例 2:

输入:nums = [1,2,2,1], limit = 2
输出:2
解释:经过 2 次操作,你可以将数组 nums 变成 [2,2,2,2] 。你不能将任何数字变更为 3 ,因为 3 > limit 。

示例 3:

输入:nums = [1,2,1,2], limit = 2
输出:0
解释:nums 已经是互补的。

 

提示:

  • n == nums.length
  • 2 <= n <= 105
  • 1 <= nums[i] <= limit <= 105
  • n 是偶数。

差分数组(Python/Java/C++/Go)

作者 endlesscheng
2026年5月9日 13:12

题目说,可以把 $\textit{nums}$ 中的任意元素变成 $[1,\textit{limit}]$ 中的整数,所以 $\textit{nums}[i]+\textit{nums}[n-1-i]$ 可以变成 $[2,\textit{limit}\cdot 2]$ 中的整数。

如果写个二重循环,先枚举 $\textit{nums}[i]+\textit{nums}[n-1-i]$ 都变成 $2,3,4,\ldots, \textit{limit}\cdot 2$,再遍历 $\textit{nums}$,计算操作次数,那么时间复杂度为 $\mathcal{O}(n\cdot \textit{limit})$,太慢了。

定义 $a[k]$ 表示 $\textit{nums}[i]+\textit{nums}[n-1-i]$ 都变成 $k$ 所需的操作次数。

横看成岭侧成峰,单看 $(\textit{nums}[i],\textit{nums}[n-1-i])$,这对元素会如何影响数组 $a$?哪些 $a[k]$ 需要增加 $1$,哪些 $a[k]$ 需要增加 $2$?

设 $x = \textit{nums}[i]$,$y = \textit{nums}[n-1-i]$。

  • 如果 $x$ 和 $y$ 都不修改,那么 $a[x+y]$ 不变。
  • 如果只修改 $y$,那么 $x+y$ 可以是 $[x+1,x+\textit{limit}]$ 中的整数;如果只修改 $x$,那么 $x+y$ 可以是 $[y+1,y+\textit{limit}]$ 中的整数。这两个区间的并集为 $[\min(x,y)+1, \max(x,y)+\textit{limit}]$。这个区间内的整数 $k$,除了 $k=x+y$ 时 $a[x+y]$ 不变,其余 $a[k]$ 都增加 $1$,表示当 $k$ 在这个区间内时,把 $x+y$ 变成 $k$ 只需操作 $1$ 次。
  • 其余在 $[2,\min(x,y)]\cup[\max(x,y)+\textit{limit}+1,\textit{limit}\cdot 2]$ 中的 $k$,把 $a[k]$ 都增加 $2$,表示当 $k$ 在这个区间内时,把 $x+y$ 变成 $k$ 需要操作 $2$ 次。

这里用到了区间增加同一个数的操作,这可以用差分数组实现,原理讲解

计算差分数组的前缀和,就得到了数组 $a$。

答案为 $\min\limits_{k=2}^{\textit{limit}\cdot 2}a[k]$。

注:题目保证 $n$ 是偶数。

优化前

###py

class Solution:
    def minMoves(self, nums: List[int], limit: int) -> int:
        n = len(nums)
        diff = [0] * (limit * 2 + 2)

        for i in range(n // 2):
            x = nums[i]
            y = nums[-1 - i]
            l = min(x, y) + 1
            r = max(x, y) + limit

            # [2, l-1] += 2
            diff[2] += 2
            diff[l] -= 2

            # [l, r] += 1
            diff[l] += 1
            diff[r + 1] -= 1

            # x+y 实际操作 0 次,从 [l, r] 中去掉
            # [x+y, x+y] -= 1
            diff[x + y] -= 1
            diff[x + y + 1] += 1

            # [r+1, limit*2] += 2
            diff[r + 1] += 2
            # limit*2+1 不在 [2, limit*2] 中,可以省略

        ans = inf
        sum_d = 0
        for d in diff[2 : limit * 2 + 1]:
            sum_d += d
            ans = min(ans, sum_d)
        return ans

###java

class Solution {
    public int minMoves(int[] nums, int limit) {
        int n = nums.length;
        int[] diff = new int[limit * 2 + 2];

        for (int i = 0; i < n / 2; i++) {
            int x = nums[i];
            int y = nums[n - 1 - i];
            int l = Math.min(x, y) + 1;
            int r = Math.max(x, y) + limit;

            // [2, l-1] += 2
            diff[2] += 2;
            diff[l] -= 2;

            // [l, r] += 1
            diff[l]++;
            diff[r + 1]--;

            // x+y 实际操作 0 次,从 [l, r] 中去掉
            // [x+y, x+y] -= 1
            diff[x + y]--;
            diff[x + y + 1]++;

            // [r+1, limit*2] += 2
            diff[r + 1] += 2;
            // limit*2+1 不在 [2, limit*2] 中,可以省略
        }

        int ans = Integer.MAX_VALUE;
        int sum = 0;
        for (int i = 2; i <= limit * 2; i++) {
            sum += diff[i];
            ans = Math.min(ans, sum);
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    int minMoves(vector<int>& nums, int limit) {
        int n = nums.size();
        vector<int> diff(limit * 2 + 2);

        for (int i = 0; i < n / 2; i++) {
            int x = nums[i];
            int y = nums[n - 1 - i];
            int l = min(x, y) + 1;
            int r = max(x, y) + limit;

            // [2, l-1] += 2
            diff[2] += 2;
            diff[l] -= 2;

            // [l, r] += 1
            diff[l]++;
            diff[r + 1]--;

            // x+y 实际操作 0 次,从 [l, r] 中去掉
            // [x+y, x+y] -= 1
            diff[x + y]--;
            diff[x + y + 1]++;

            // [r+1, limit*2] += 2
            diff[r + 1] += 2;
            // limit*2+1 不在 [2, limit*2] 中,可以省略
        }

        int ans = INT_MAX;
        int sum = 0;
        for (int i = 2; i <= limit * 2; i++) {
            sum += diff[i];
            ans = min(ans, sum);
        }
        return ans;
    }
};

###go

func minMoves(nums []int, limit int) int {
n := len(nums)
diff := make([]int, limit*2+2)
for i, x := range nums[:n/2] {
y := nums[n-1-i]
l := min(x, y) + 1
r := max(x, y) + limit

// [2, l-1] += 2
diff[2] += 2
diff[l] -= 2

// [l, r] += 1
diff[l]++
diff[r+1]--

// x+y 实际操作 0 次,从 [l, r] 中去掉
// [x+y, x+y] -= 1
diff[x+y]--
diff[x+y+1]++

// [r+1, limit*2] += 2
diff[r+1] += 2
// limit*2+1 不在 [2, limit*2] 中,可以省略
}

ans := math.MaxInt
sum := 0
for _, d := range diff[2 : limit*2+1] {
sum += d
ans = min(ans, sum)
}
return ans
}

优化

  • diff[2] += 2 执行了 $\dfrac{n}{2}$ 次,相当于 $\textit{diff}[2]$ 整体增加了 $n$。也可以初始化 $\textit{sum}=n$。
  • diff[l] -= 2diff[l] += 1 合并为 diff[l] -= 1
  • diff[r+1] -= 1diff[r+1] += 2 合并为 diff[r+1] += 1

###py

class Solution:
    def minMoves(self, nums: List[int], limit: int) -> int:
        n = len(nums)
        diff = [0] * (limit * 2 + 2)

        for i in range(n // 2):
            x = nums[i]
            y = nums[n - 1 - i]
            l = min(x, y) + 1
            r = max(x, y) + limit
            diff[l] -= 1
            diff[x + y] -= 1
            diff[x + y + 1] += 1
            diff[r + 1] += 1

        ans = inf
        sum_d = n
        for d in diff[2 : limit * 2 + 1]:
            sum_d += d
            ans = min(ans, sum_d)
        return ans

###java

class Solution {
    public int minMoves(int[] nums, int limit) {
        int n = nums.length;
        int[] diff = new int[limit * 2 + 2];

        for (int i = 0; i < n / 2; i++) {
            int x = nums[i];
            int y = nums[n - 1 - i];
            int l = Math.min(x, y) + 1;
            int r = Math.max(x, y) + limit;
            diff[l]--;
            diff[x + y]--;
            diff[x + y + 1]++;
            diff[r + 1]++;
        }

        int ans = Integer.MAX_VALUE;
        int sum = n;
        for (int i = 2; i <= limit * 2; i++) {
            sum += diff[i];
            ans = Math.min(ans, sum);
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    int minMoves(vector<int>& nums, int limit) {
        int n = nums.size();
        vector<int> diff(limit * 2 + 2);

        for (int i = 0; i < n / 2; i++) {
            int x = nums[i];
            int y = nums[n - 1 - i];
            int l = min(x, y) + 1;
            int r = max(x, y) + limit;
            diff[l]--;
            diff[x + y]--;
            diff[x + y + 1]++;
            diff[r + 1]++;
        }

        int ans = INT_MAX;
        int sum = n;
        for (int i = 2; i <= limit * 2; i++) {
            sum += diff[i];
            ans = min(ans, sum);
        }
        return ans;
    }
};

###go

func minMoves(nums []int, limit int) int {
n := len(nums)
diff := make([]int, limit*2+2)
for i, x := range nums[:n/2] {
y := nums[n-1-i]
l := min(x, y) + 1
r := max(x, y) + limit
diff[l]--
diff[x+y]--
diff[x+y+1]++
diff[r+1]++
}

ans := math.MaxInt
sum := n
for _, d := range diff[2 : limit*2+1] {
sum += d
ans = min(ans, sum)
}
return ans
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n+ \textit{limit})$,其中 $n$ 是 $\textit{nums}$ 的长度。
  • 空间复杂度:$\mathcal{O}(\textit{limit})$。

专题训练

见下面差分题单的「§2.1 一维差分」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

借这个问题学习一下差分数组,O(n) 算法

作者 liuyubobobo
2020年11月29日 12:35

假设 res[x] 表示的是,nums[i] + nums[n - 1 - i]x 的时候,需要多少次操作。

我们只需要计算出所有的 x 对应的 res[x], 取最小值就好了。

根据题意,nums[i] + nums[n - 1 - i] 最小是 2,即将两个数都修改为 1;最大是 2 * limit,即将两个数都修改成 limit

所以,res[x]x 的取值范围是 [2, 2 * limit]。我们用一个 res[2 * limit + 1] 的数组就好。


关键是,如何求出每一个 res[x] 位置的值,即修改后互补的数字和为 x,需要多少操作?

为了叙述方便,假设 nums[i]Anums[n - 1 - i]B

显然有:

  • 如果修改后两个数字的和是 A + B,我们使用的操作数是 0 (没有修改));

  • 否则的话,如果修改后两个数字和在 [1 + min(A, B), limit + max(A, B)] 的范围,我们使用的操作数是 1 (只需要修改 A 或者 B 就好);

  • 否则的话,如果修改后两个数字和在 [2, 2 * limit] 的范围,我们使用的操作数是 ``2`(两个数字都要修改));


所以,我们的算法是遍历每一组 nums[i]nums[n - 1 - i],然后:

  • 先将 [2, 2 * limit] 的范围需要的操作数 + 2

  • 之后,将 [1 + min(A, B), limit + max(A, B)] 的范围需要的操作数 - 1(即 2 - 1 = 1,操作 1 次);

  • 之后,将 [A + B] 位置的值再 -1(即 1 - 1 = 0,操作 0 次)。

可以看出,整个过程都是在做区间更新。最后,我们查询每一个位置的值,取最小值就好。


对于这个需求,我们当然可以使用线段树或者树状数组解决,但代码量稍大,且复杂度也是 O(nlogn) 的。

但是仔细观察,我们发现,我们只需要作区间更新,和单点查询。

对于这个需求,有一种非常常规的”数据结构“,叫差分数组,完全满足需求,并且编程极其简单,整体可以在 O(n) 的时间解决。

打上引号,是因为差分数组就是一个数组而已。


简单来说,差分数组 diff[i],存储的是 res[i] - res[i - 1];而差分数组 diff[0...i] 的和,就是 res[i] 的值。

大家可以用一个小数据试验一下,很好理解。


如果我们想给 [l, r] 的区间加上一个数字 a, 只需要 diff[l] += a,diff[r + 1] -= a

这样做,diff[0...i] 的和,就是更新后 res[i] 的值。

依然是,大家可以用一个小数据试验一下,其实很好理解。


有了差分数组这个武器,这个问题就很简单了。

我的参考代码如下(C++):

class Solution {
public:
    int minMoves(vector<int>& nums, int limit) {
        
        // 差分数组, diff[0...x] 的和表示最终互补的数字和为 x,需要的操作数
        // 因为差分数组的计算需要更新 r + 1,所以数组的总大小在 limit * 2 + 1 的基础上再 + 1
        vector<int> diff(limit * 2 + 2, 0);

        int n = nums.size();
        for(int i = 0; i < n / 2; i ++){
            int A = nums[i], B = nums[n - 1 - i];

            // [2, 2 * limit] 范围 + 2
            int l = 2, r = 2 * limit;
            diff[l] += 2, diff[r + 1] -= 2;

            // [1 + min(A, B), limit + max(A, B)] 范围 -1
            l = 1 + min(A, B), r = limit + max(A, B);
            diff[l] += -1, diff[r + 1] -= -1;

            // [A + B] 再 -1    
            l = A + B, r = A + B;
            diff[l] += -1, diff[r + 1] -= -1;
        }

        // 依次求和,得到 最终互补的数字和 i 的时候,需要的操作数 sum
        // 取最小值
        int res = n, sum = 0;
        for(int i = 2; i <= 2 * limit; i ++){
            sum += diff[i];
            if(sum < res) res = sum;
        }
        return res;
    }
};

整体时间复杂度是 O(n) 的;空间复杂度也是 O(n) 的。


觉得有帮助请点赞哇!

差分+扫描

作者 lucifer1004
2020年11月29日 12:10

本场周赛题解 | 我的LeetCode比赛题解

我们考虑任意一个数对$(a,b)$,不妨假设$a\leq b$。假设最终选定的和值为$target$,则我们可以发现,对于$(a,b)$这个数对:

  • 当$target<1+a$时,需要操作两次;
  • 当$1+a\leq target<a+b$时,需要操作一次;
  • 当$target=a+b$时,不需要操作;
  • 当$a+b<target\leq b+limit$时,需要操作一次;
  • 当$target>b+limit$时,需要操作两次。

我们设初始操作次数为两次。令$target$从数轴最左端开始向右移动,我们会发现:

  • 在$1+a$处,操作次数减少一次;
  • 在$a+b$处,操作次数减少一次;
  • 在$a+b+1$处,操作次数增加一次;
  • 在$b+limit+1$处,操作次数增加一次。

因此,我们可以遍历数组中的所有数对,求出每个数对的这四个关键位置,然后更新差分数组。最后,我们遍历(扫描)差分数组,就可以找到令总操作次数最小的$target$,同时可以得到最少的操作次数。

  • 时间复杂度$O(N+L)$。
  • 空间复杂度$O(L)$。

###cpp

class Solution {
public:
    int minMoves(vector<int>& nums, int limit) {
        int n = nums.size();
        vector<int> delta(limit * 2 + 2);
        for (int i = 0; i < n / 2; ++i) {
            int lo = 1 + min(nums[i], nums[n - i - 1]);
            int hi = limit + max(nums[i], nums[n - i - 1]);
            int sum = nums[i] + nums[n - i - 1];
            delta[lo]--;
            delta[sum]--;
            delta[sum + 1]++;
            delta[hi + 1]++;
        }
        int now = n;
        int ans = n;
        for (int i = 2; i <= limit * 2; ++i) {
            now += delta[i];
            ans = min(ans, now);
        }
        return ans;
    }
};
昨天 — 2026年5月12日首页

牵头打造的全国统一电力交易App上线?中电联全面否认

2026年5月12日 20:59
5月12日,中国电力企业联合会在官网公开发声称,该会未参与“中电交易App”研发、打造和发布等事宜。声明指出,近日,中国电力企业联合会发现有互联网媒体平台发布“中电交易App正式上线全国统一电力市场建设迈出决定性一步”“全国统一电力交易所中电交易App正式落地我国电力市场化改革迈入新纪元”“中电交易App上线,售电公司的跨省生意终于好做了”或类似标题文章,宣称“中电交易App”由我会(中国电力企业联合会/中电联)牵头打造或参与研发等表述。以上涉及中国电力企业联合会与“中电交易App”相关联的任何内容均不属实。(澎湃)

万科A:就220亿股东借款与深铁集团签署补充协议

2026年5月12日 20:55
36氪获悉,万科A公告,公司于2025年11月20日召开的2025年第一次临时股东会审议通过了《关于就深铁集团向公司提供股东借款并由公司提供担保订立框架协议的议案》,同意公司与第一大股东深圳市地铁集团有限公司签署《关于股东借款及资产担保的框架协议》。根据《框架协议》,在2025年以来至公司2025年度股东会召开日为止期间深铁集团向公司提供不超过220亿元借款额度,公司将对借款额度下实际发生的借款提供抵/质押担保(简称“原220亿股东借款事项”)。公司于2026年5月12日与深铁集团就《框架协议》签署《关于股东借款及资产担保的框架协议之补充协议》,就原220亿股东借款事项下的担保方式、担保物、抵/质押率等相关安排进行了补充约定。

4连板宝鼎科技:目前未有高速覆铜板M7和M9产品销售

2026年5月12日 20:44
36氪获悉,4连板宝鼎科技发布股票交易异常波动公告称,公司主要产品为覆铜板、电子铜箔及黄金采选。其中覆铜板(CCL)主要为玻纤布基覆铜板(FR-4)、复合基覆铜板等常规产品,目前未有高速覆铜板M7和M9产品销售,无相关订单和营业收入;电子铜箔产品分为高温高延伸性铜箔(HTE)、低轮廓铜箔(LP)、反转处理铜箔(RTF)及超低轮廓铜箔(HVLP),其中HVLP铜箔目前尚处于客户认证及市场拓展阶段,未形成批量生产,2025年度HVLP铜箔销量占公司铜箔销量比例仅为0.03%,短期内对公司整体经营业绩贡献有限,未来订单获取及业务发展存在不确定性。

SK海力士回应进入兴福电子前十大流通股东:系上市前投资

2026年5月12日 20:30
SK海力士就一季度进入兴福电子前十大流通股东向记者表示,对兴福电子的投资是公司在其上市前已完成投资的项目,该件事宜为公司对上市前的初创企业的投资。注:兴福电子在5月11日股价异动公告中就该事项回应,SK海力士(无锡)投资有限公司系公司首次公开发行前股东,持有公司股份数量500万股,已于2026年1月22日解除限售。(证券时报)

*ST凯鑫:股票交易撤销退市风险警示,明起停牌一天

2026年5月12日 20:12
36氪获悉,*ST凯鑫公告,公司符合申请撤销退市风险警示的条件,关于撤销退市风险警示的申请已获得深交所审核同意。公司股票将于2026年5月13日停牌一天,并于5月14日开市起复牌,自复牌起撤销退市风险警示,证券简称由“*ST凯鑫”变更为“上海凯鑫”。

宏德股份:拟定增募资不超5.6亿元

2026年5月12日 20:04
36氪获悉,宏德股份公告,公司本次向特定对象发行A股股票拟募集资金总额不超过5.6亿元(含本数),扣除发行费用后募集资金拟投资于高端装备关键铝合金部件智能化制造及配套能力提升项目以及补充流动资金。
❌
❌