Depending upon the application, the number of reads/writes, etc, as provided by juergen d you can use a lookup table to store the pairing information.
If you have a limited set of categories (64 or less) then you have the option of avoiding the overhead of the lookup table and use a SET field. It denormalises, but means that you can retrieve the categories along with the product from a single row.
You can then also do bitwise comparison to find products that are in a certain category/set of categories, e.g.
mysql> show create table products\G
*************************** 1. row ***************************
Table: products
Create Table: CREATE TABLE `products` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
`product_categories` set('a','b','c') DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `product_categories` (`product_categories`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> insert into products set name = 'product a', product_categories = 'a';
Query OK, 1 row affected (0.00 sec)
mysql> insert into products set name = 'product b', product_categories = 'b';
Query OK, 1 row affected (0.01 sec)
mysql> insert into products set name = 'product ab', product_categories = 'a,b';
Query OK, 1 row affected (0.01 sec)
mysql> select id, name from products where product_categories & 1;
+----+------------+
| id | name |
+----+------------+
| 1 | product a |
| 3 | product ab |
+----+------------+
2 rows in set (0.00 sec)
mysql> select id, name from products where product_categories & 2;
+----+------------+
| id | name |
+----+------------+
| 2 | product b |
| 3 | product ab |
+----+------------+
2 rows in set (0.00 sec)
mysql> select id, name from products where product_categories = 'a';
+----+-----------+
| id | name |
+----+-----------+
| 1 | product a |
+----+-----------+
1 row in set (0.00 sec)